1 READ ME

SUMMARY
This R code is used to estimate the relationship between oxygen consumption (MO₂) and ambient oxygen partial pressure (PO₂) in the Common Galaxias (Galaxias maculatus). It also estimates the critical partial pressure of oxygen for aerobic metabolism (Pcrit), which is commonly understood as the threshold below which the oxygen consumption rate can no longer be sustained. The associated article is “The role of osmorespiratory compromise in hypoxia tolerance of the purportedly oxyconforming teleost Galaxias maculatus.” If you click the “Code” button in the top right of the HTML document, you can download the Rmd file.

AIM
The article aims to test whether Galaxias maculatus can maintain oxygen consumption (MO₂) as ambient PO₂ falls and, if so, at what level it reaches the critical partial pressure of oxygen for aerobic metabolism (Pcrit).

AUTHORS
To be added

AFFILIATIONS
To be added

AIM
To be added

Disclaimer
I (Jake Martin) am dyslexic. I have made an effort to review the script for spelling errors, but some will likely remain. I apologise. Please reach out via the contact details below if anything is unclear.

2 Knit settings

These are the settings for the html output. We will use the html to make out index file on Git

#kniter seetting
knitr::opts_chunk$set(
message = FALSE,
warning = FALSE, # no warnings
cache = TRUE,# Cacheing to save time when kniting
tidy = TRUE
)

4 Required packages

These are the R packages required for this script. You will need to install a package called pacman to run the p_load function.

# this installs and load packages
# need to install pacman
pacman::p_load("ggplot2", 
               "ggthemes", 
               "ggfortify", 
               "gtExtras", 
               "igraph",
               "dagitty",
               "ggdag",
               "ggridges",
               "gghalves",
               "ggExtra",
               "gridExtra",
               "corrplot",
               "RColorBrewer", 
               "gt", 
               "gtsummary",
               "grid",
               "plotly",
               "bayesplot", # data visualisation
               
                "tidyverse", 
               "janitor", 
               "readxl", 
               "broom.mixed", 
               "data.table", 
               "devtools",
               "hms", # data tidy
               
               "marginaleffects", 
               "brms", 
               "rstan", 
               "performance", 
               "emmeans", 
               "tidybayes", 
               "vegan",
               "betareg",
               "lme4", 
               "car", 
               "lmerTest",
               "qqplotr",
               "respirometry",
               "mclust",
               "furrr", # modelling 
              
               
               "datawizard", 
               "SRS", # data manipulation 
               
               "sessioninfo" # reproducibility
                       )

5 Functions (custom)

Here are some custom function used within this script.

bayes_incremental_regression_by_id(): A custom function to build Bayesian incremental regressions. It is formatted for running a list of sub group models (ids) in parallel with 4 cores. It uses brm() with a Gaussian error function.

Use: The function takes an grouping factor/id that will be used to filter the data for the regression, if none is proved it will use all data (id_i), the column name of the grouping factor/charter in the data frame, if none is proved it will use all data (id_name), the data frame (data), the predictor of interest (predictor), the response of interest (response), the random seed number for model reproducibility (seed_number), where you would like to save the model outputs (save_models = logical argument), the output directory for the saved rds models (mod_output_wd)

bayes_incremental_regression_by_id(id_i = “your_id”, id_name = “column name for ids”, data = “your data”, predictor = “predictor variable”, response = “response variable”, seed_number = integer, save_models = TRUE/FALSE, mod_output_wd = “directory for model output”)

# Instead we could also use a distributional regression approach, by
# specifically modelling the variance by DO (e.g. sigma ~ DO). Weighting may
# not be required in this case, I don't think higher density of vaules in a
# given space will effect Bayesian estimates like it does in frequentist
# models. See discourse https://discourse.mc-stan.org/t/weights-in-brm/4278

bayes_incremental_regression_by_id <- function(id_i, id_name, data, predictor, response,
    seed_number, save_models, mod_output_wd) {
    # Initiate an empty list to store models
    models <- list()

    # Check if id_name is missing, NULL, or blank, and assign NA if so
    if (missing(id_name) || is.null(id_name) || id_name == "") {
        id_name <- NA
    }

    # Check if id_i is missing, NULL, or blank, and assign NA if so
    if (missing(id_i) || is.null(id_i) || id_i == "") {
        id_name <- NA
    }

    # Filter data for the current ID if id_name is given as a factor or
    # character and id_i is defined
    df_i <- data %>%
        dplyr::filter(if (!is.na(id_i) && (is.factor(data[[id_name]]) || is.character(data[[id_name]]))) {
            !!rlang::sym(id_name) == id_i
        } else {
            TRUE
        })

    # Dynamically create formulas
    formula_lm_0 <- reformulate("1", response)
    formula_lm_1 <- reformulate(predictor, response)
    formula_lm_2 <- reformulate(sprintf("poly(%s, 2)", predictor), response)
    formula_lm_3 <- reformulate(sprintf("poly(%s, 3)", predictor), response)

    # Fit and store models in the list
    models[[paste0(id_i, "_lm_0")]] <- brm(bf(formula_lm_0, family = gaussian()),
        data = df_i, cores = 4, seed = seed_number, save_pars = save_pars(all = save_models),
        sample_prior = FALSE, silent = TRUE, file = paste0(mod_output_wd, "/", id_i,
            "_lm_0"))

    models[[paste0(id_i, "_lm_1")]] <- brm(bf(formula_lm_1, family = gaussian()),
        data = df_i, cores = 4, seed = seed_number, save_pars = save_pars(all = save_models),
        sample_prior = FALSE, silent = TRUE, file = paste0(mod_output_wd, "/", id_i,
            "_lm_1"))

    models[[paste0(id_i, "_lm_2")]] <- brm(bf(formula_lm_2, family = gaussian()),
        data = df_i, cores = 4, seed = seed_number, save_pars = save_pars(all = save_models),
        sample_prior = FALSE, silent = TRUE, file = paste0(mod_output_wd, "/", id_i,
            "_lm_2"))

    models[[paste0(id_i, "_lm_3")]] <- brm(bf(formula_lm_3, family = gaussian()),
        data = df_i, cores = 4, seed = 143019, save_pars = save_pars(all = save_models),
        sample_prior = FALSE, silent = TRUE, file = paste0(mod_output_wd, "/", id_i,
            "_lm_3"))

    # Return the list of models for the current ID
    return(models)
}

load_rds9(): A custom function to load all rds models in a directory and store in a list. Takes a directory with ‘.rds’ files

load_rds <- function(model_dw) {
    # List all .rds files in the directory
    model_file_list <- list.files(path = model_dw, pattern = "\\.rds$", full.names = TRUE)

    # Initialise an empty list to store models
    model_store_list <- list()

    # Iterate through each file and load the RDS
    for (mod_i in model_file_list) {
        mod <- readRDS(file = mod_i)  # Read the RDS file
        model_name <- tools::file_path_sans_ext(basename(mod_i))  # Extract the file name without extension
        model_store_list[[model_name]] <- mod  # Store it in the list
    }

    # Return the list of models
    return(model_store_list)
}

incremental_regression_bayes_fits(): A custom function for pulling model fits, loo and r2 using loo() and bayes_R2(), respectively. Takes a list of models.

# Define Function to Process the data for each ID
incremental_regression_bayes_fits <- function(models) {
  
  loo_results_list <- list()
  
  # Iterate over the names of the models
  for (mod_name in names(models)) {
    # Extract the model
    mod_i <- models[[mod_name]]
    
    # Compute LOO results
    mod_loo_results_i <- loo::loo(mod_i)
    
    # Extract relevant LOO metrics
    elpd_loo_i <- mod_loo_results_i$elpd_loo
    p_loo_i <- mod_loo_results_i$p_loo
    looic_i <- mod_loo_results_i$looic
    
    # Create a data frame with metrics
    df_i <- data.frame(
      elpd_loo = elpd_loo_i,
      p_loo = p_loo_i,
      looic = looic_i,
      model = mod_name
    )
    
    est_i <- tidy(mod_i, effects = "fixed", conf.int = TRUE) %>% 
      dplyr::select(term, estimate, conf.low, conf.high) %>% 
      tidyr::pivot_wider(
        names_from = term,                # Use `term` as column names
        values_from = c(estimate, conf.low, conf.high),  # Values to pivot
        names_sep = "_"                   # Add a separator to column names
      )
    
    df_i <- cbind(df_i, est_i)
    
    # Store the data frame in the list
    loo_results_list[[mod_name]] <- df_i
  }
  
  # Combind 
  loo_results_combined <- bind_rows(loo_results_list)
  
  # Get R2 
  r2_results <- map_dfr(models, ~ as.data.frame(bayes_R2(.x)), .id = "model") %>%
    tibble::remove_rownames()
  
  # Combind R2 and loo results 
  model_fit_df <- dplyr::full_join(loo_results_combined, r2_results, by = "model") %>% 
    dplyr::select(model, everything()) %>% 
    dplyr::rename(r2 = Estimate,
                  r2_error = Est.Error,
                  r2_q2.5 = Q2.5,
                  r2_q97.5 = Q97.5) %>% 
    dplyr::mutate(id = sub("_(lm_\\d+)$", "", model),
                  model_type = sub("^.*_(lm_\\d+)$", "\\1", model))
  
  return(model_fit_df)
}

bayes_mod_predictions(): This function extracts the predicted values using fitted() from a list of models and combines them with the original data file used for the model. These are the posterior mean fitted values (i.e., the expected value of the response variable given the predictor variables and the estimated posterior distributions of the parameters) for each observation in the dataset, along with 95% credible intervals.

bayes_mod_predictions <- function(models, original_data) {

    prediction_list <- list()

    for (mod_name in names(models)) {
        # Extract mod
        mod_i <- models[[mod_name]]

        # Make mode predictions
        model_predictions_i <- as.data.frame(fitted(mod_i, summary = TRUE)) %>%
            dplyr::mutate(model = mod_name, id = sub("_(lm_\\d+)$", "", mod_name),
                model_type = sub("^.*_(lm_\\d+)$", "\\1", mod_name)) %>%
            dplyr::rename(pred_lower = Q2.5, pred_upper = Q97.5, predicted = Estimate,
                pred_error = Est.Error) %>%
            dplyr::select(model, everything())

        id_i <- model_predictions_i$id[1]

        original_data_i <- original_data %>%
            dplyr::filter(id == id_i)

        model_predictions_original_i <- cbind(model_predictions_i, original_data_i)

        prediction_list[[mod_name]] <- model_predictions_original_i
    }
    predictions_df <- bind_rows(prediction_list)
    return(predictions_df)
}

calcSMR(): authored by Denis Chabot used to estimate SMR with several different methods Claireaux and Chabot (2016) [1]

[1] Claireaux, G. and Chabot, D. (2016) Responses by fishes to environmental hypoxia: integration through Fry’s concept of aerobic metabolic scope. Journal of Fish Biology https://doi.org/10.1111/jfb.12833

calcSMR = function(Y, q = c(0.1, 0.15, 0.2, 0.25, 0.3), G = 1:4) {
    u = sort(Y)
    the.Mclust <- Mclust(Y, G = G)
    cl <- the.Mclust$classification
    # sometimes, the class containing SMR is not called 1 the following
    # presumes that when class 1 contains > 10% of cases, it contains SMR,
    # otherwise we take class 2
    cl2 <- as.data.frame(table(cl))
    cl2$cl <- as.numeric(levels(cl2$cl))
    valid <- cl2$Freq >= 0.1 * length(time)
    the.cl <- min(cl2$cl[valid])
    left.distr <- Y[the.Mclust$classification == the.cl]
    mlnd = the.Mclust$parameters$mean[the.cl]
    CVmlnd = sd(left.distr)/mlnd * 100
    quant = quantile(Y, q)
    low10 = mean(u[1:10])
    low10pc = mean(u[6:(5 + round(0.1 * (length(u) - 5)))])
    # remove 5 outliers, keep lowest 10% of the rest, average Herrmann & Enders
    # 2000
    return(list(mlnd = mlnd, quant = quant, low10 = low10, low10pc = low10pc, cl = cl,
        CVmlnd = CVmlnd))
}

calcO2crit(): authored by Denis Chabot used to estimate O2crit (Pcript). Claireaux and Chabot (2016) [1]

[1] Claireaux, G. and Chabot, D. (2016) Responses by fishes to environmental hypoxia: integration through Fry’s concept of aerobic metabolic scope. Journal of Fish Biology https://doi.org/10.1111/jfb.12833

Note: O2 is assumed to be in percentage of dissolved oxygen (DO)

calcO2crit <- function(Data, SMR, lowestMO2 = NA, gapLimit = 4, max.nb.MO2.for.reg = 20) {
    # AUTHOR: Denis Chabot, Institut Maurice-Lamontagne, DFO, Canada first
    # version written in June 2009 last updated in January 2015
    method = "LS_reg"  # will become 'through_origin' if intercept is > 0
    if (is.na(lowestMO2))
        lowestMO2 = quantile(Data$MO2[Data$DO >= 80], p = 0.05)
    # Step 1: identify points where MO2 is proportional to DO
    geqSMR = Data$MO2 >= lowestMO2
    pivotDO = min(Data$DO[geqSMR])
    lethal = Data$DO < pivotDO
    N_under_SMR = sum(lethal)  # points available for regression?
    final_N_under_SMR = lethal  # some points may be removed at Step 4
    lastMO2reg = Data$MO2[Data$DO == pivotDO]  # last MO2 when regulating
    if (N_under_SMR > 1)
        theMod = lm(MO2 ~ DO, data = Data[lethal, ])
    # Step 2, add one or more point at or above SMR 2A, when there are fewer
    # than 3 valid points to calculate a regression
    if (N_under_SMR < 3) {
        missing = 3 - sum(lethal)
        not.lethal = Data$DO[geqSMR]
        DOlimit = max(sort(not.lethal)[1:missing])  # highest DO acceptable
        # to reach a N of 3
        addedPoints = Data$DO <= DOlimit
        lethal = lethal | addedPoints
        theMod = lm(MO2 ~ DO, data = Data[lethal, ])
    }
    # 2B, add pivotDO to the fit when Step 1 yielded 3 or more values?
    if (N_under_SMR >= 3) {
        lethalB = Data$DO <= pivotDO  # has one more value than 'lethal'
        regA = theMod
        regB = lm(MO2 ~ DO, data = Data[lethalB, ])
        large_slope_drop = (coef(regA)[2]/coef(regB)[2]) > 1.1  # arbitrary
        large_DO_gap = (max(Data$DO[lethalB]) - max(Data$DO[lethal])) > gapLimit
        tooSmallMO2 = lastMO2reg < SMR
        if (!large_slope_drop & !large_DO_gap & !tooSmallMO2)
            {
                lethal = lethalB
                theMod = regB
            }  # otherwise we do not accept the additional point
    }
    # Step 3 if the user wants to limit the number of points in the regression
    if (!is.na(max.nb.MO2.for.reg) & sum(lethal) > max.nb.MO2.for.reg) {
        Ranks = rank(Data$DO)
        lethal = Ranks <= max.nb.MO2.for.reg
        theMod = lm(MO2 ~ DO, data = Data[lethal, ])
        final_N_under_SMR = max.nb.MO2.for.reg
    }
    # Step 4
    predMO2 = as.numeric(predict(theMod, data.frame(DO = Data$DO)))
    Data$delta = (Data$MO2 - predMO2)/predMO2 * 100  # residuals set to zero
    # when below pivotDO
    Data$delta[Data$DO < pivotDO | lethal] = 0
    tol = 0  # any positive residual is unacceptable
    HighValues = Data$delta > tol
    Ranks = rank(-1 * Data$delta)
    HighMO2 = HighValues & Ranks == min(Ranks)  # keep largest residual
    if (sum(HighValues) > 0)
        {
            nblethal = sum(lethal)
            Data$W = NA
            Data$W[lethal] = 1/nblethal
            Data$W[HighMO2] = 1
            theMod = lm(MO2 ~ DO, weight = W, data = Data[lethal | HighMO2, ])
            # This new regression is always an improvement, but there can still
            # be points above the line, so we repeat
            predMO2_2 = as.numeric(predict(theMod, data.frame(DO = Data$DO)))
            Data$delta2 = (Data$MO2 - predMO2_2)/predMO2_2 * 100
            Data$delta2[Data$DO < pivotDO] = 0
            tol = Data$delta2[HighMO2]
            HighValues2 = Data$delta2 > tol
            if (sum(HighValues2) > 0)
                {
                  Ranks2 = rank(-1 * Data$delta2)
                  HighMO2_2 = HighValues2 & Ranks2 == 1  # keep the largest residual
                  nblethal = sum(lethal)
                  Data$W = NA
                  Data$W[lethal] = 1/nblethal
                  Data$W[HighMO2_2] = 1
                  theMod2 = lm(MO2 ~ DO, weight = W, data = Data[lethal | HighMO2_2,
                    ])
                  # is new slope steeper than the old one?
                  if (theMod2$coef[2] > theMod$coef[2]) {
                    theMod = theMod2
                    HighMO2 = HighMO2_2
                  }
                }  # end second search for high value
        }  # end first search for high value
    Coef = coefficients(theMod)
    # Step 5, check for positive intercept
    AboveOrigin = Coef[1] > 0
    # if it is, we use a regression that goes through the origin
    if (AboveOrigin) {
        theMod = lm(MO2 ~ DO - 1, data = Data[lethal, ])
        Coef = c(0, coefficients(theMod))  # need to add the intercept (0)
        # manually to have a pair of coefficients
        method = "through_origin"
        HighMO2 = rep(FALSE, nrow(Data))  # did not use the additional value
        # from Step 4
    }
    po2crit = as.numeric(round((SMR - Coef[1])/Coef[2], 1))
    sum_mod = summary(theMod)
    anov_mod = anova(theMod)
    O2CRIT = list(o2crit = po2crit, SMR = SMR, Nb_MO2_conforming = N_under_SMR, Nb_MO2_conf_used = final_N_under_SMR,
        High_MO2_required = sum(HighMO2) == 1, origData = Data, Method = method,
        mod = theMod, r2 = sum_mod$r.squared, P = anov_mod$"Pr(>F)", lethalPoints = which(lethal),
        AddedPoints = which(HighMO2))
}  # end function

plotO2crit(): authored by Denis Chabot, used to plot the modes used for the calcO2crit() function. Claireaux and Chabot (2016) [1]

Note: I added abline(h=lowestMO2, col=“pink”) so that I could visualise the lowestMO2 position

plotO2crit <- function(o2critobj, plotID = "", Xlab = "Dissolved oxygen (% sat.)",
    Ylab = "dotitalumol", smr.cex = 0.9, o2crit.cex = 0.9, plotID.cex = 1.2, Transparency = T,
    ...) {
    # AUTHOR: Denis Chabot, Institut Maurice-Lamontagne, DFO, Canada first
    # version written in June 2009 last updated 2015-02-09 for R plotting
    # devices that do not support transparency (e.g., postscript), set
    # Transparency to FALSE
    smr = o2critobj$SMR
    if (Ylab %in% c("dotitalumol", "italumol", "dotumol", "umol", "dotitalmg", "italmg",
        "dotmg", "mg")) {
        switch(Ylab, dotitalumol = {
            mo2.lab = expression(paste(italic(dot(M))[O[2]], " (", mu, "mol ", O[2],
                " ", min^-1, " ", kg^-1, ")"))
        }, italumol = {
            mo2.lab = expression(paste(italic(M)[O[2]], " (", mu, "mol ", O[2], " ",
                min^-1, " ", kg^-1, ")"))
        }, dotumol = {
            mo2.lab = expression(paste(dot(M)[O[2]], " (", mu, "mol ", O[2], " ",
                min^-1, " ", kg^-1, ")"))
        }, umol = {
            mo2.lab = expression(paste(M[O[2]], " (", mu, "mol ", O[2], " ", min^-1,
                " ", kg^-1, ")"))
        }, dotitalmg = {
            mo2.lab = expression(paste(italic(dot(M))[O[2]], " (mg ", O[2], " ",
                h^-1, " ", kg^-1, ")"))
        }, italmg = {
            mo2.lab = expression(paste(italic(M)[O[2]], " (mg ", O[2], " ", h^-1,
                " ", kg^-1, ")"))
        }, dotmg = {
            mo2.lab = expression(paste(dot(M)[O[2]], " (mg ", O[2], " ", h^-1, " ",
                kg^-1, ")"))
        }, mg = {
            mo2.lab = expression(paste(M[O[2]], " (mg ", O[2], " ", h^-1, " ", kg^-1,
                ")"))
        })
    } else mo2.lab = Ylab
    if (Transparency) {
        Col = c(rgb(0, 0, 0, 0.7), "red", "orange")
    } else {
        Col = c(grey(0.3), "red", "orange")
    }
    Data = o2critobj$origData
    lowestMO2 = quantile(Data$MO2[Data$DO >= 80], p = 0.05)  # I added this
    Data$Color = Col[1]
    Data$Color[o2critobj$lethalPoints] = Col[2]
    Data$Color[o2critobj$AddedPoints] = Col[3]
    # ordinary LS regression without added points: blue line, red symbols
    # ordinary LS regression with added points: blue line, red & orange symbols
    # regression through origin: green dotted line, red symbols
    line.color = ifelse(o2critobj$Method == "LS_reg", "blue", "darkgreen")
    line.type = ifelse(o2critobj$Method == "LS_reg", 1, 3)
    limX = c(0, max(Data$DO))
    limY = c(0, max(Data$MO2))
    plot(MO2 ~ DO, data = Data, xlim = limX, ylim = limY, col = Data$Color, xlab = Xlab,
        ylab = mo2.lab, ...)
    coord <- par("usr")
    if (plotID != "") {
        text(0, coord[4], plotID, cex = plotID.cex, adj = c(0, 1.2))
    }
    abline(h = lowestMO2, col = "pink")  # I added this
    abline(h = smr, col = "orange")
    text(coord[1], smr, "SMR", adj = c(-0.1, 1.3), cex = smr.cex)
    text(coord[1], smr, round(smr, 1), adj = c(-0.1, -0.3), cex = smr.cex)
    if (!is.na(o2critobj$o2crit)) {
        abline(o2critobj$mod, col = line.color, lty = line.type)
        segments(o2critobj$o2crit, smr, o2critobj$o2crit, coord[3], col = line.color,
            lwd = 1)
        text(x = o2critobj$o2crit, y = 0, o2critobj$o2crit, col = line.color, cex = o2crit.cex,
            adj = c(-0.1, 0.5))
    }
}  # end of function

6 Working directories

6.1 Input

meta_files_wd: Directory for the metadata

wd <- getwd()
meta_files_wd <- paste0(wd, "./meta-data")  # creates a variable with the name of the wd we want to use

labchart_wd: Directory for Labchart estimated slopes

labchart_wd <- paste0(wd, "./lab-chart-slopes")

mod_data_wd: Directory for model output data estimated slopes

mod_data_wd <- paste0(wd, "./mod-data")

6.2 Output

output_fig_wd: this is where we will put the figures

## [1] "Folder already exists"

output_mods_wd: this is where we will put the models

## [1] "Folder already exists"

7 Input files

7.1 Slopes (MO2)

slope_df: We have imported the slopes extracted in LabChart during each phase of the experiment

 setwd(labchart_wd)
# 
# # Get the names of all sheets in the Excel file
sheet_names <- excel_sheets("labchart-all-dates_v2.xlsx")
all_trials_select <- c("start_date", "order", "phase", "cycle", "date", "time")
slope_list <- list()

for (sheet in sheet_names) {

  df <- read_excel("labchart-all-dates_v2.xlsx", sheet = sheet) %>% 
  dplyr::rename_with(tolower)
  
a_name <- paste0("a_", tolower(sheet))
a_df <- df %>%
  dplyr::select(starts_with('a'), all_trials_select) %>% 
  dplyr::rename(temp = a_temp) %>% 
  dplyr::mutate(across(starts_with('a'), as.numeric)) %>% 
  pivot_longer(
    cols = starts_with('a'), # Select all columns to pivot
    names_to = c("chamber_id", ".value"), # Separate column names into 'id' and other variables
    names_sep = "_"
  ) %>%
  dplyr::mutate(respirometer_group = "a") # Add a new column with a fixed value

slope_list[[a_name]]<- a_df

b_name <- paste0("b_", tolower(sheet))
b_df <- df %>% 
  dplyr::select(starts_with('b'), all_trials_select) %>% 
  dplyr::rename(temp = b_temp) %>% 
  dplyr::mutate(across(starts_with('b'), as.numeric)) %>% 
  pivot_longer(
    cols = starts_with('b'), # Select all columns to pivot
    names_to = c("chamber_id", ".value"), # Separate column names into 'id' and other variables
    names_sep = "_"
  ) %>% 
    dplyr::mutate(respirometer_group = "b")

slope_list[[b_name]] <- b_df

c_name <- paste0("c_", tolower(sheet))
c_df <- df %>% 
  dplyr::select(starts_with('c'), all_trials_select) %>% 
  dplyr::rename(temp = c_temp,
                i_cycle = cycle) %>% 
  dplyr::mutate(across(starts_with('c'), as.numeric)) %>%
  pivot_longer(
    cols = starts_with('c'), # Select all columns to pivot
    names_to = c("chamber_id", ".value"), # Separate column names into 'id' and other variables
    names_sep = "_"
  ) %>% 
    dplyr::mutate(respirometer_group = "c") %>% 
  dplyr::rename(cycle = i_cycle)

slope_list[[c_name]] <- c_df

d_name <- paste0("d_", tolower(sheet))
d_df <- df %>% 
  dplyr::select(starts_with('d'), all_trials_select) %>% 
  dplyr::rename(temp = d_temp,
                i_date = date) %>% 
  dplyr::mutate(across(starts_with('d'), as.numeric)) %>%
  pivot_longer(
    cols = starts_with('d'), # Select all columns to pivot
    names_to = c("chamber_id", ".value"), # Separate column names into 'id' and other variables
    names_sep = "_"
  ) %>% 
    dplyr::mutate(respirometer_group = "d") %>% 
  dplyr::rename(date = i_date)

slope_list[[d_name]] <- d_df
}


slope_df <- bind_rows(slope_list) %>% 
  dplyr::mutate(resp_cat_date = paste0(respirometer_group, "_", start_date),
                chamber_n = str_extract(chamber_id, "\\d+"),
                id_prox = paste0(resp_cat_date, "_", chamber_n),
                time_hms = as_hms(time*3600),
                date_chr = format(date, "%d/%m/%Y")
                )

7.2 Metadata

metadata: This is the meta data for each chamber

Note: We are also adding volume based on chamber type.

setwd(meta_files_wd)

metadata <- read_excel("Morpho.xlsx", na = "NA") %>%
    dplyr::mutate(id_split = id) %>%
    tidyr::separate(id_split, into = c("respirometer_group", "salinity_group", "start_date",
        "chamber"), sep = "_") %>%
    dplyr::mutate(volume = dplyr::case_when(chamber_type == "L" ~ 0.3, chamber_type ==
        "M_M" ~ 0.105, chamber_type == "M_NM" ~ 0.11, chamber_type == "S" ~ 0.058,
        chamber_type == "SM" ~ 0.075, chamber_type == "D3" ~ 0.055, TRUE ~ NA), id_prox = paste0(respirometer_group,
        "_", start_date, "_", chamber), rel_size = mass/volume)

7.2.1 Urbina, Glover, and Forster (2012)

urbina_et_al_2012: This is the mean level data extracted from Urbina, Glover, and Forster (2012)[2] Figure 1a. We used the metaDigitise package in R to extract the data [3].

setwd(meta_files_wd)

urbina_et_al_2012 <- read.csv("urbina-et-al-2012-fig1a.csv")

7.2.2 Combinding metadata

Adding the meta data to LabChart slopes

slope_df_2 <- slope_df %>%
    dplyr::select(-start_date, -respirometer_group) %>%
    left_join(metadata, by = "id_prox") %>%
    dplyr::mutate(light_dark = if_else(time_hms >= as.hms("07:00:00") & time_hms <
        as.hms("19:00:00"), "light", "dark")) %>%
    dplyr::arrange(id)

8 Data

8.1 Numbers

We have 64 fish with MO2 data

n <- slope_df_2 %>%
    dplyr::filter(chamber_condition == "fish") %>%
    dplyr::distinct(id) %>%
    nrow(.)

paste0("n = ", n)
## [1] "n = 64"

With 48 from the 0 ppt and 48 from 9 ppt groups

slope_df_2 %>%
    dplyr::filter(chamber_condition == "fish") %>%
    dplyr::group_by(salinity_group) %>%
    dplyr::reframe(`n total` = length(unique(id))) %>%
    gt() %>%
    cols_label(salinity_group = "Salinity group") %>%
    cols_align(align = "center", columns = everything())
Salinity group n total
0 33
9 31

8.2 Fish size

Here we calculate the mean length and size of fish used in the experiment.

mass_length <- slope_df_2 %>%
    dplyr::filter(chamber_condition == "fish") %>%
    dplyr::group_by(id) %>%
    dplyr::sample_n(1) %>%
    dplyr::ungroup() %>%
    dplyr::reframe(x_mass = round(mean(mass, na.rm = TRUE), 3), min_mass = round(min(mass,
        na.rm = TRUE), 3), max_mass = round(max(mass, na.rm = TRUE), 3), x_length = round(mean(length,
        na.rm = TRUE), 2), min_length = round(min(length, na.rm = TRUE), 2), max_length = round(max(length,
        na.rm = TRUE), 2))

mass_mean <- mass_length %>%
    pull(x_mass)

mass_min <- mass_length %>%
    pull(min_mass)

mass_max <- mass_length %>%
    pull(max_mass)

length_mean <- mass_length %>%
    pull(x_length)

length_min <- mass_length %>%
    pull(min_length)

length_max <- mass_length %>%
    pull(max_length)

paste0("The mean mass of fish was ", mass_mean, " g (range: ", mass_min, "-", mass_max,
    ")", ", and the mean length was ", length_mean, " mm (range: ", length_min, "-",
    length_max, ")")
## [1] "The mean mass of fish was 0.532 g (range: 0.21-1.6), and the mean length was 50.41 mm (range: 40-70)"

Here we calculate the mean length and size of fish used in the experiment

mass_rel_length <- slope_df_2 %>%
    dplyr::filter(chamber_condition == "fish") %>%
    dplyr::group_by(id) %>%
    dplyr::sample_n(1) %>%
    dplyr::ungroup()  #%>% 
# dplyr::reframe(mean_rel_size = round(mean(rel_size, na.rm = TRUE), 3),
# min_rel_size = round(min(rel_size, na.rm = TRUE), 3), max_rel_size =
# round(max(rel_size, na.rm = TRUE), 3)) rel_size_mean <- mass_rel_length %>%
# pull(mean_rel_size) rel_size_min <- mass_rel_length %>% pull(min_rel_size)
# rel_size_max <- mass_rel_length %>% pull(max_rel_size) paste0('The mean
# relative size of fish to thier respirometry chamber (mass g per volume L) was
# ', rel_size_mean, ' (range: ', rel_size_min, '-', rel_size_max, ')')

8.3 Filtering trials

We will remove 6 trials which had errors. These are as follows:

  • a_0_25nov_3 fish died during trial
  • b_0_26nov_4 flat lined early
  • c_0_22nov_2 chamber was opened early
  • c_9_26nov_2 chamber was opened early
  • c_9_26nov_4 chamber was opened early
  • d_9_27nov_3 sensor was jumpy and end points were hard to confidently ID visually
remove_trial_error <- c("a_0_25nov_3", "b_0_26nov_4", "c_0_22nov_2", "c_9_26nov_2",
    "c_9_26nov_4", "d_9_27nov_3")

slope_df_filter <- slope_df_2 %>%
    dplyr::filter(!(id %in% remove_trial_error))

We now have 58 fish with MO2 data

n <- slope_df_filter %>%
    dplyr::filter(chamber_condition == "fish") %>%
    dplyr::distinct(id) %>%
    nrow(.)

paste0("n = ", n)
## [1] "n = 58"

With 30 in the 0 ppt group and 28 in the 9 ppt group

slope_df_filter %>%
    dplyr::filter(chamber_condition == "fish") %>%
    dplyr::group_by(salinity_group) %>%
    dplyr::reframe(`n total` = length(unique(id))) %>%
    gt() %>%
    cols_label(salinity_group = "Salinity group") %>%
    cols_align(align = "center", columns = everything())
Salinity group n total
0 30
9 28

8.4 Filtering MO2 estimates

Here we apply the following filters to the MO2 data:

  • Remove the first 5 SMR cycles (burn in)
  • Remove all positive raw slopes
  • Remove all MO2 calculated using less then 60 data points (5 min)
  • Remove all MO2 calculated if O2 increases in a closed phase (i.e. trial has ended)

Check positive values for MO2 before removing.

slope_tidy_remove_flush <- slope_df_filter %>%
  dplyr::filter(phase != "smr", chamber_condition == "fish") %>% 
  dplyr::group_by(id) %>%
  dplyr::arrange(id, order) %>%  # Ensure the data is sorted within each group
  dplyr::mutate(o2_diff = if_else(row_number() == 1, 0, o2 - lag(o2)), # Calculate the difference in 'o2'
                o2_diff_cumsum = cumsum(o2_diff > 1)) %>%  # Checks first occurrence and sums 
  dplyr::filter(o2_diff_cumsum == 0) %>%  # Keep rows until the first jump > 1
  dplyr::ungroup() %>%
  dplyr::select(-o2_diff, -o2_diff_cumsum)

postive_slopes <- slope_tidy_remove_flush %>%
  dplyr::filter(chamber_condition == "fish" & mo2corr > 0)

list_postive_all <- slope_df_filter %>% 
  dplyr::filter(chamber_condition == "fish" & mo2corr > 0 & phase == "smr") %>% 
  dplyr::bind_rows(., postive_slopes) %>% 
  dplyr::distinct(id) %>% 
  dplyr::pull(id)

print(paste0("There are ", length(list_postive_all), " fish with postive slopes. These fish are: ", paste(list_postive_all, collapse = ", "), "."))
## [1] "There are 15 fish with postive slopes. These fish are: a_9_22nov_1, d_0_21nov_3, a_0_24nov_1, b_0_24nov_3, b_9_21nov_2, b_9_21nov_3, b_9_22nov_1, b_9_22nov_2, b_9_22nov_3, c_0_21nov_1, c_0_21nov_2, c_0_22nov_4, c_9_24nov_3, d_0_22nov_2, d_9_25nov_3."

Filtering the MO2 data

cycle_burn <- 0:4

slope_df_filter_1 <- slope_df_filter %>%
  dplyr::filter(!(cycle %in% cycle_burn) & 
                  mo2corr < 0 & 
                  n > 60 &
                  chamber_condition == "fish"
                )
  
# Filter out the end flush
slope_tidy_closed <- slope_df_filter_1 %>%
  dplyr::filter(phase != "smr") %>% 
  dplyr::group_by(id) %>%
  dplyr::arrange(id, order) %>%  # Ensure the data is sorted within each group
  dplyr::mutate(o2_diff = if_else(row_number() == 1, 0, o2 - lag(o2)), # Calculate the difference in 'o2'
                o2_diff_cumsum = cumsum(o2_diff > 1)) %>%  # Checks first occurrence and sums 
  dplyr::filter(o2_diff_cumsum == 0) %>%  # Keep rows until the first jump > 1
  dplyr::ungroup() %>%
  dplyr::select(-o2_diff, -o2_diff_cumsum)

slope_tidy_smr <- slope_df_filter_1 %>% 
  dplyr::filter(phase == "smr")

slope_df_filter_2 <- rbind(slope_tidy_smr, slope_tidy_closed) %>% 
  dplyr::arrange(id, order)

8.5 Calculating SMR

We will calculate SMR using calcSMR function by Chabot, Steffensen and Farrell (2016)[1]. Specifically, we use mean of the lowest normal distribution (MLND) where CVmlnd < 5.4 and the mean of the lower 20% quantile (q0.2) were CVmlnd > 5.4. If CVmlnd is not calculated we have used q0.2.

labchart_chabot_smr <- slope_df_filter_2 %>%
    dplyr::filter(phase == "smr")

# Extract distinct IDs
ids <- labchart_chabot_smr %>%
    dplyr::distinct(id) %>%
    dplyr::pull()

# Initialise an empty list to store SMR data
smr_list <- list()

# Process each ID
for (id_i in ids) {
    tryCatch({
        # Filter the data for the current ID
        df_i <- labchart_chabot_smr %>%
            dplyr::filter(id == id_i) %>%
            dplyr::mutate(abs_mo2corr = abs(mo2corr))

        # Calculate SMR results
        calcSMR_results <- calcSMR(df_i$abs_mo2corr)
        CVmlnd_i <- calcSMR_results$CVmlnd
        quant_i <- calcSMR_results$quant %>%
            as_tibble()
        quant_20per_i <- quant_i$value[3]
        mlnd_i <- calcSMR_results$mlnd
        smr_value <- if_else(CVmlnd_i < 5.4, mlnd_i, quant_20per_i)
        smr_type <- if_else(CVmlnd_i < 5.4, "mlnd", "quant_20per")
        smr_value <- if_else(is.na(smr_value), quant_20per_i, smr_value)
        smr_type <- if_else(is.na(smr_type), "quant_20per", smr_type)

        # Create a data frame for the current ID
        smr_df <- tibble(id = id_i, smr = smr_value, smr_est = smr_type)

    }, error = function(e) {
        # Handle errors by assigning NA values
        smr_df <- tibble(id = id_i, smr = NA, smr_est = NA)
    })

    # Append to the list
    smr_list[[id_i]] <- smr_df
}

# Combine all individual SMR data frames into one
smr_df <- bind_rows(smr_list) %>%
    dplyr::rename(smr_chabot = smr, smr_chabot_method = smr_est)

slope_df_filter_3 <- slope_df_filter_2 %>%
    dplyr::left_join(., smr_df, by = "id")

8.6 Transforming MO2

Here we are transforming the MO2 units. The resulting values are as follows:

  • MO2 is absolute value of the background and leak corrected MO2 slope from Labchart (mo2corr) times the net volume of the chamber (volume - fish mass), × 60, × 60, to achieve MO2 as mg-1 O2 h-1

  • MO2_g is MO2 divided by fish mass to achieve MO2 as mg-1 O2 g-1 h-1 (i.e. mass standardised)

  • SMR absolute value of the SMR estimates using methods described by Chabot, Steffensen and Farrell (2016)[1] times the net volume of the chamber (volume - fish mass), × 60, × 60, to achieve SMR as mg-1 O2 g-1 h-1)

  • SMR_g is SMR divided by fish mass to achieve SMR as mg-1 O2 g-1 h-1 (i.e. mass standardised)

  • DO is dissolved oxygen percentage calculated from O2 values (mg-1 L-1) using the recorded temperature, salinity, and a constant atmospheric pressure (Pa; 1013.25)

  • o2_kpa is the O2 concentration in kilopascal (kpa). This is used to make a comparative figure only.

# Combine back into one data frame
slope_tidy <- slope_df_filter_3 %>% 
    dplyr::mutate(DO = conv_o2(
                   o2 = o2,
                   from = "mg_per_l",
                   to = "percent_a.s.",
                   temp = temp, #C
                   sal = measured_salinity,
                   atm_pres = 1013.25),
                  o2_kpa = conv_o2(
                   o2 = o2,
                   from = "mg_per_l",
                   to = "kPa",
                   temp = temp, #C
                   sal = measured_salinity,
                   atm_pres = 1013.25),
                  net_volume = volume - mass/1000,
                  MO2 = abs(mo2corr)*net_volume*60*60,
                  MO2_g = MO2/mass,
                  SMR = abs(smr_chabot)*net_volume*60*60,
                  SMR_g = SMR/mass
                  )

9 Visualising data

9.1 O2 vs MO2

9.1.1 Figure Si: Metabolic rate by dissolved oxygen percentage

This was used to identify any outliers, or potential errors.

lm_lines <- slope_tidy %>%
  group_by(id) %>%
  summarise(
    DO_seq = list(seq(min(DO), max(DO), length.out = 100)),  # Generate a sequence of DO values
    MO2_pred = list(predict(lm(MO2_g ~ DO, data = cur_data()), newdata = data.frame(DO = seq(min(DO), max(DO), length.out = 100))))
  ) %>%
  unnest(c(DO_seq, MO2_pred))  # Expand lists into rows

# Create scatter plot with markers for each fish
p <- plot_ly(
  data = slope_tidy,
  x = ~DO,
  y = ~MO2_g,
  type = "scatter",
  mode = "markers",
  color = ~id,  # Colour points by fish ID
  marker = list(opacity = 0.6),
  name = ~id
)

# Add regression lines for each fish
p <- p %>%
  add_trace(
    data = lm_lines,
    x = ~DO_seq,
    y = ~MO2_pred,
    type = "scatter",
    mode = "lines",
    color = ~id,  # Ensure each line matches its corresponding fish
    line = list(width = 1, dash = "solid"),
    showlegend = FALSE  # Avoid cluttering the legend
  )

# Final layout
p <- p %>%
  layout(
    title = "MO<sub>2</sub> vs Dissolved Oxygen with individual linear regressions",
    xaxis = list(title = "Dissolved Oxygen (%)"),
    yaxis = list(title = "MO<sub>2</sub> (mg<sup>-1</sup> O<sub>2</sub> g<sup>-1</sup> h<sup>-1</sup>)"),
    showlegend = FALSE
  )

# Display plot
p

Figure Si: interactive plot of metabolic rate measurements (MO2; mg O2 g-1h-1) by dissolved oxygen percentage (DO) for all fish, including all estimates during the SMR phase (i.e. intermittent phase)

9.1.2 Figure Si: Metabolic rate by dissolved oxygen percentage for salinity

Looking at the difference responses in the two salinity groups.

slope_tidy %>% 
  ggplot(aes(y = MO2_g, x = DO, colour = id)) + # Default aesthetics
  geom_point(show.legend = FALSE) +
  geom_smooth(aes(group = id), method = "lm", se = FALSE, colour = scales::alpha("black", 0.5)) + # Transparent black lines
  geom_smooth(method = "lm", se = TRUE, colour = "red") + # Overall smooth line
  geom_smooth(se = TRUE, colour = "red", linetype = "dashed") +
  theme_clean() +
  facet_wrap(~salinity_group) +
  labs(
    subtitle = "mo2 vs o2 by salinity treatment",
    x = "Dissolved oxygen percentage (DO)",
    y = "MO2 (O2 mg/g/h)"
  )

Figure Si:

9.1.3 Figure Si: Metabolic rate by dissolved oxygen percentage for chamber type

Looking at the different chamber types

slope_tidy %>% 
  ggplot(aes(y = MO2_g, x = DO, colour = id)) + # Default aesthetics
  geom_point(show.legend = FALSE) +
  geom_smooth(aes(group = id), method = "lm", se = FALSE, colour = scales::alpha("black", 0.5)) + # Transparent black lines
  geom_smooth(method = "lm", se = TRUE, colour = "red") + # Overall smooth line
  geom_smooth(se = TRUE, colour = "red", linetype = "dashed") +
  theme_clean() +
  facet_wrap(~chamber_type, scale = "free") +
  labs(
    subtitle = "mo2 vs o2 by chamber type",
    x = "Dissolved oxygen percentage (DO)",
    y = "MO2 (mg O2 g/h)"
  )

Figure Si:

Here, we have recreated a similar plot to that presented in Urbina, Glover, and Forster (2012)[1] and have extracted the mean level data from Figure 1a using the metaDigitise package in R[3]. This data is called urbina_et_al_2012. This allows us to compare the differences in Mo2 and the relationship between Mo2 and O2.

First making a binned data frame

min_o2_kpa <- min(slope_tidy$o2_kpa, na.rm = TRUE)
max_o2_kpa <- max(slope_tidy$o2_kpa, na.rm = TRUE)

time_bin_df <- slope_tidy %>%
  mutate(o2_group = cut(o2_kpa, 
                        breaks = seq(min_o2_kpa, max_o2_kpa, length.out = 13), # 11 intervals, so 12 breakpoints
                        labels = paste0("Group ", 1:12), 
                        include.lowest = TRUE)) %>% 
  dplyr::group_by(o2_group) %>% 
  dplyr::reframe(mean_MO2_g = mean(MO2_g)*31.25,
                 mean_o2_kpa = mean(o2_kpa),
                 n = length(MO2_g)*31.25,
                 MO2_g_sd = sd(MO2_g)*31.25,
                 o2_kpa_sd = sd(o2_kpa))

Figure Si:

9.1.4 Figure Si: Metabolic rate by oxygen concentration for O2 bins with Urbina (2012)

Now the plot with our mean data and the mean data from Urbina, Glover, and Forster (2012)[1]

n <- slope_tidy %>%
    dplyr::distinct(id) %>%
    nrow(.)

# Existing plot
p <- time_bin_df %>%
    ggplot() + geom_point(data = slope_tidy, aes(y = MO2_g * 31.25, x = o2_kpa),
    size = 2, color = "grey", alpha = 0.3) + geom_point(data = time_bin_df, aes(y = mean_MO2_g,
    x = mean_o2_kpa), size = 3, colour = "#0E4C92", show.legend = FALSE) + geom_errorbar(data = time_bin_df,
    aes(y = mean_MO2_g, x = mean_o2_kpa, ymin = mean_MO2_g - MO2_g_sd, ymax = mean_MO2_g +
        MO2_g_sd), width = 0.15, colour = "#0E4C92") + geom_errorbarh(data = time_bin_df,
    aes(y = mean_MO2_g, x = mean_o2_kpa, xmin = mean_o2_kpa - o2_kpa_sd, xmax = mean_o2_kpa +
        o2_kpa_sd), height = 0.4, colour = "#0E4C92") + geom_point(data = urbina_et_al_2012,
    aes(x = o2_mean, y = mo2_mean), size = 3, shape = 21, fill = "#D21F3C", color = "#D21F3C",
    stroke = 1) + geom_errorbar(data = urbina_et_al_2012, aes(x = o2_mean, ymin = mo2_mean -
    mo2_sd, ymax = mo2_mean + mo2_sd), width = 0.2, colour = "#D21F3C") + geom_errorbarh(data = urbina_et_al_2012,
    aes(y = mo2_mean, xmin = o2_mean - o2_sd, xmax = o2_mean + o2_sd), height = 0.4,
    colour = "#D21F3C") + annotate("text", x = 0, y = 16, label = bquote(atop("Current data (blue), " *
    italic(n) * " = " * .(n), "Urbina data (red), " * italic(n) * " = 67")), hjust = 0,
    vjust = 1, size = 4) + theme_clean() + labs(subtitle = "", x = "PO2 (kPa)", y = "MO2 (umol O2 g/h)") +
    scale_y_continuous(limits = c(0, 16), breaks = seq(0, 16, by = 2)) + scale_x_continuous(limits = c(0,
    22), breaks = seq(0, 22, by = 2))

p

Figure S1: Metabolic rate (MO2) by oxygen concentration for O2 bins with Urbina (2012)[1]

9.2 Routine MO2

Making an SMR phase only data frame

slope_tidy_smr <- slope_tidy %>%
    dplyr::filter(phase == "smr")

9.2.1 Figure Si: Routine MO2 by saility

mean_mo2_salinity <- slope_tidy_smr %>% 
  dplyr::group_by(salinity_group) %>% 
  dplyr::reframe(mean_mo2 = mean(MO2, na.rm = TRUE))

fig_i <- ggplot() +
    geom_violin(data = slope_tidy_smr, aes(x = salinity_group, y = MO2, fill = salinity_group), color = NA, alpha = 0.3) +
  geom_jitter(data = slope_tidy_smr, aes(x = salinity_group, y = MO2, fill = salinity_group),
                       shape = 21, size = 2, color = "black", alpha = 0.2) +
  geom_boxplot(data = slope_tidy_smr, aes(x = salinity_group, y = MO2, fill = salinity_group),
                        size = 1, alpha = 0.5, outlier.shape = NA, width = 0.3) +
  geom_point(data = mean_mo2_salinity, 
                aes(x = salinity_group, y = mean_mo2, fill = salinity_group), 
                size = 3, alpha = 0.8, colour = "black", stroke = 2) +
  scale_fill_manual(values = c("#4B5320", "#000080")) +  # Custom fill colours
  scale_colour_manual(values = c("#4B5320", "#000080")) +
  theme_clean() +
  theme(legend.position = "none") +
  labs(
    subtitle = "",
    x = "Salinity group (ppt)",
    y = "Routine MO2 (mg O2 g/h)"
  )

fig_i

Figure Si: Plot of all MO2 measures during SMR phase by salinity treatment. The small points are the observed values, the shaded area behind the points is the a kernel density of the observed data, the box plot on top show the median and interquartile range (IQR).

9.3 SMR

9.3.1 Figure Si: SMR by saility

Here’s the same plot but for only the SMR, as estimated with calcSMR function by Chabot, Steffensen and Farrell (2016)[1]. Specifically, we use mean of the lowest normal distribution (MLND) where CVmlnd < 5.4 and the mean of the lower 20% quantile (q0.2) were CVmlnd > 5.4. If CVmlnd is not calculated we have used q0.2.

smr_only <- slope_tidy_smr %>% 
  dplyr::group_by(id) %>% 
  dplyr::slice(1)

mean_smr_salinity <- smr_only %>% 
  dplyr::group_by(salinity_group) %>% 
  dplyr::reframe(mean_smr = mean(SMR, na.rm = TRUE))

fig_i <- ggplot() +
    geom_violin(data = smr_only, aes(x = salinity_group, y = SMR, fill = salinity_group), color = NA, alpha = 0.3) +
  geom_jitter(data = smr_only, aes(x = salinity_group, y = SMR, fill = salinity_group),
                       shape = 21, size = 2, color = "black", alpha = 0.2) +
  geom_boxplot(data = smr_only, aes(x = salinity_group, y = SMR, fill = salinity_group),
                        size = 1, alpha = 0.5, outlier.shape = NA, width = 0.3) +
  geom_point(data = mean_smr_salinity, 
                aes(x = salinity_group, y = mean_smr, fill = salinity_group), 
                size = 3, alpha = 0.8, colour = "black", stroke = 2) +
  scale_fill_manual(values = c("#4B5320", "#000080")) +  # Custom fill colours
  scale_colour_manual(values = c("#4B5320", "#000080")) +
  theme_clean() +
  theme(legend.position = "none") +
  labs(
    subtitle = "",
    x = "Salinity group (ppt)",
    y = "MO2 (mg O2 g/h)"
  )

fig_i

9.3.2 Figure Si: All fish SMR and closed MO2 vaules

Create output directory if needed

ids <- slope_tidy %>%
    dplyr::distinct(id) %>%
    pull(id) %>%
    as.list()

MO2_plot_list <- list()

# 1) Open the PDF device once
pdf(file = file.path(output_fig_slopes_wd, "combined_slopes.pdf"), width = 8, height = 6)

# 2) Loop over IDs and create each plot
for (id_i in ids) {

    smr <- slope_tidy %>%
        dplyr::filter(id == id_i) %>%
        dplyr::slice(1) %>%
        dplyr::pull(SMR)

    plot <- slope_tidy %>%
        dplyr::filter(id == id_i) %>%
        ggplot(aes(x = o2, y = MO2)) + geom_hline(yintercept = smr, linetype = "dashed",
        color = "darkred") + geom_point(aes(colour = phase)) + theme_clean() + labs(subtitle = paste0(id_i,
        " slopes"), x = "Mean o2 (mg_per_l)", y = "abs(mo2) (mg_per_l)")

    # Instead of saving each plot separately, just print it
    print(plot)

    MO2_plot_list[[id_i]] <- plot
}

# 3) Close the PDF device *after* the loop
dev.off()
## png 
##   2
for (p in MO2_plot_list) {
    print(p)
}

Figure Si: MO2 by O2 for each fish with the SMR represented as a dashed red

10 Analysis

10.1 Routine MO2

10.1.1 Scaling predictors

Here we scale our predictors for the model

scale_list <- c("temp", "order", "mass")

slope_tidy_smr <- slope_tidy_smr %>%
    dplyr::mutate(across(all_of(scale_list), ~scale(.x, center = TRUE, scale = FALSE),
        .names = "{.col}_z"), light_dark_c = if_else(light_dark == "light", 0.5,
        -0.5))

10.1.2 Model structure

Here we will use a Bayesian Generalised Linear Mixed Model (GLMM) with a Gamma distribution and a log link, where the shape parameter (K) is also modelled as a function of predictors. This models MO2 by salinity during the SMR phase to see if the fish held at different salinities have different SMRs. We have also added a few scaled predictors, that may help describe variation in the data, such as mass (g; 0.21–1.6) temperature (°C; 13.841–14.277), measurement order (1–28), and light/dark cycle (light or dark; light between 07:00:00 and 19:00:00), we also include a random effect for fish id to account for multiple MO2 measures on each fish (1 - 58). We allowed the the shape parameter (K) to vary as a function of some of the predictors (e.g. salinity_group, order_z) to improve fit.

mo2_gamma_bf <- bf(MO2 ~ temp_z + order_z + light_dark_c + mass_z + salinity_group +
    (1 | id), shape ~ salinity_group + order_z, family = Gamma(link = "log"))

10.1.3 Prior selection

These are the default priors. We will use these.

default_prior <- get_prior(mo2_gamma_bf, data = slope_tidy_smr, family = Gamma(link = "log"))
default_prior
##                    prior     class            coef group resp  dpar nlpar lb ub
##                   (flat)         b                                             
##                   (flat)         b    light_dark_c                             
##                   (flat)         b          mass_z                             
##                   (flat)         b         order_z                             
##                   (flat)         b salinity_group9                             
##                   (flat)         b          temp_z                             
##  student_t(3, -2.7, 2.5) Intercept                                             
##     student_t(3, 0, 2.5)        sd                                         0   
##     student_t(3, 0, 2.5)        sd                    id                   0   
##     student_t(3, 0, 2.5)        sd       Intercept    id                   0   
##                   (flat)         b                            shape            
##                   (flat)         b         order_z            shape            
##                   (flat)         b salinity_group9            shape            
##     student_t(3, 0, 2.5) Intercept                            shape            
##        source
##       default
##  (vectorized)
##  (vectorized)
##  (vectorized)
##  (vectorized)
##  (vectorized)
##       default
##       default
##  (vectorized)
##  (vectorized)
##       default
##  (vectorized)
##  (vectorized)
##       default

Using generic standardised weakly informative priors for all estimates (b)

priors_custom <- c(set_prior("normal(0, 1)", class = "b"), set_prior("student_t(3, 0, 10)",
    class = "b", dpar = "shape"), set_prior("student_t(3, 0, 2.5)", class = "sd"),
    set_prior("student_t(3, -2.8, 2.5)", class = "Intercept"), set_prior("student_t(3, 0, 2.5)",
        class = "Intercept", dpar = "shape"))
priors_custom
##                    prior     class coef group resp  dpar nlpar   lb   ub source
##             normal(0, 1)         b                             <NA> <NA>   user
##      student_t(3, 0, 10)         b                 shape       <NA> <NA>   user
##     student_t(3, 0, 2.5)        sd                             <NA> <NA>   user
##  student_t(3, -2.8, 2.5) Intercept                             <NA> <NA>   user
##     student_t(3, 0, 2.5) Intercept                 shape       <NA> <NA>   user

10.1.4 Run model

Here we run the model, I have hashed this out because I have saved the model for quick reloading.

## [1] "Model complete"

Prior only model

Here we reload the model

setwd(output_mods_wd)

mo2_mod_gamma <- readRDS(file = "mo2_mod_gamma.rds")
# mo2_mod_gamma_prior <- readRDS(file = 'mo2_mod_gamma_prior.rds')

10.1.5 Model diagnostics

Checking model convergence

color_scheme_set("red")
plot(mo2_mod_gamma, ask = F)

Checking rhat are equal to one

summary(mo2_mod_gamma)
##  Family: gamma 
##   Links: mu = log; shape = log 
## Formula: MO2 ~ temp_z + order_z + light_dark_c + mass_z + salinity_group + (1 | id) 
##          shape ~ salinity_group + order_z
##    Data: slope_tidy_smr (Number of observations: 895) 
##   Draws: 4 chains, each with iter = 8000; warmup = 1000; thin = 2;
##          total post-warmup draws = 14000
## 
## Multilevel Hyperparameters:
## ~id (Number of levels: 58) 
##               Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
## sd(Intercept)     0.33      0.03     0.27     0.40 1.00     3788     6587
## 
## Regression Coefficients:
##                       Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS
## Intercept                -2.55      0.06    -2.67    -2.43 1.00     2577
## shape_Intercept           2.57      0.07     2.43     2.70 1.00    12311
## temp_z                   -0.34      0.22    -0.77     0.08 1.00     6520
## order_z                   0.00      0.00    -0.00     0.00 1.00    11627
## light_dark_c              0.13      0.02     0.08     0.17 1.00    12338
## mass_z                    1.24      0.17     0.91     1.57 1.00     3326
## salinity_group9          -0.10      0.09    -0.27     0.08 1.00     2313
## shape_salinity_group9     0.18      0.10    -0.02     0.38 1.00    11729
## shape_order_z            -0.04      0.01    -0.06    -0.02 1.00    11486
##                       Tail_ESS
## Intercept                 4682
## shape_Intercept          12782
## temp_z                    9206
## order_z                  11778
## light_dark_c             11954
## mass_z                    5857
## salinity_group9           3563
## shape_salinity_group9    12124
## shape_order_z            11950
## 
## Draws were sampled using sampling(NUTS). For each parameter, Bulk_ESS
## and Tail_ESS are effective sample size measures, and Rhat is the potential
## scale reduction factor on split chains (at convergence, Rhat = 1).

Looking at prior and predicted distributions for our parameters

# keep_parameters <- c('b_Intercept', 'b_shape_Intercept', 'b_temp_z',
# 'b_order_z', 'b_light_dark_c', 'b_mass_z', 'b_salinity_group9',
# 'b_shape_salinity_group9') prior_samples <-
# as_draws_df(mo2_mod_gamma_prior_2) posterior_samples <-
# as_draws_df(mo2_mod_gamma_2) posterior_samples$source <- 'Posterior'
# prior_samples$source <- 'Prior' combined_samples <-
# bind_rows(posterior_samples, prior_samples) # Convert to long format for
# ggplot long_samples <- pivot_longer(combined_samples, cols = -source,
# names_to = 'parameter', values_to = 'value') %>% dplyr::filter(parameter %in%
# keep_parameters) ggplot(long_samples, aes(x = value, fill = source)) +
# geom_density(alpha = 0.5) + # Overlay density plots facet_wrap(~parameter,
# scales = 'free') + # Separate by parameter labs(title = 'Prior vs Posterior
# Distributions', x = 'Parameter Value', y = 'Density') + theme_minimal()

Using leave one out (loo) measure of fit, the model appears to preform well, all Pareto k estimates are good (k < 0.7)

loo(mo2_mod_gamma)
## 
## Computed from 14000 by 895 log-likelihood matrix.
## 
##          Estimate   SE
## elpd_loo   2256.5 38.6
## p_loo        71.5  7.0
## looic     -4513.0 77.2
## ------
## MCSE of elpd_loo is 0.1.
## MCSE and ESS estimates assume MCMC draws (r_eff in [0.7, 1.0]).
## 
## All Pareto k estimates are good (k < 0.7).
## See help('pareto-k-diagnostic') for details.

Model predictions generally align with the observed data

color_scheme_set("red")
p <- pp_check(mo2_mod_gamma, type = "dens_overlay")
p

10.1.6 Results

We did not see a meaningful difference between the routine metabolic rate for fish from the two salinity treatments.

10.1.6.1 Table Si

Table Si: Fixed effect Estimates (β) and 95% Credible Intervals (95% CI)

model_est <- fixef(mo2_mod_gamma, probs = c(0.025, 0.975)) %>%
    as.data.frame() %>%
    tibble::rownames_to_column(var = "Predictor") %>%
    dplyr::mutate(β = round(Estimate, 3), Q2.5 = round(Q2.5, 3), Q97.5 = round(Q97.5,
        3), `95% CI` = paste0("[", Q2.5, ", ", Q97.5, "]"))

model_est %>%
    dplyr::select(Predictor, "β", "95% CI") %>%
    gt()
Predictor β 95% CI
Intercept -2.551 [-2.673, -2.431]
shape_Intercept 2.568 [2.43, 2.7]
temp_z -0.344 [-0.768, 0.076]
order_z 0.001 [-0.002, 0.005]
light_dark_c 0.128 [0.08, 0.174]
mass_z 1.237 [0.908, 1.566]
salinity_group9 -0.096 [-0.272, 0.082]
shape_salinity_group9 0.179 [-0.02, 0.379]
shape_order_z -0.042 [-0.061, -0.024]

Looking at the marginal mean difference between salinity groups

em_results <- emmeans(mo2_mod_gamma, ~salinity_group)
contrast_results <- contrast(em_results, method = "pairwise")
em_results_df <- em_results %>%
    tidy() %>%
    mutate(across(where(is.numeric), ~exp(.)))
contrast_results_df <- contrast_results %>%
    tidy() %>%
    mutate(across(where(is.numeric), ~exp(.)))


em_results_df %>%
    gt()
salinity_group estimate lower.HPD upper.HPD
0 0.07805491 0.06933489 0.08823359
9 0.07092322 0.06228137 0.08069857

Pulling the emmeans draws for our plot

emmeans_draws <- mo2_mod_gamma %>%
    emmeans(~salinity_group) %>%
    gather_emmeans_draws() %>%
    dplyr::mutate(.value = exp(.value), salinity_group = as.character(salinity_group))

emmeans_contrast_draws <- mo2_mod_gamma %>%
    emmeans(~salinity_group) %>%
    contrast(method = "pairwise") %>%
    gather_emmeans_draws() %>%
    dplyr::mutate(.value = exp(.value))

10.1.6.2 Figure Si

NOTE: This plot is in the main text of the manuscript as Figure 1a

mean_mo2_salinity <- slope_tidy_smr %>% 
  dplyr::group_by(salinity_group) %>% 
  dplyr::reframe(mean_mo2 = mean(MO2, na.rm = TRUE))


fig_1 <- ggplot() +
  geom_violin(data = slope_tidy_smr,
              aes(x = salinity_group, y = MO2, fill = salinity_group),
              color = NA, alpha = 0.2) +
  geom_jitter(data = slope_tidy_smr,
              aes(x = salinity_group, y = MO2, fill = salinity_group),
              shape = 21, width = 0.3, size = 1, color = "black", alpha = 0.1) +
    geom_point(data = mean_mo2_salinity,
             aes(x = salinity_group, y = mean_mo2, fill = salinity_group),
             size = 4, alpha = 1, stroke = 2, color = "black", shape = 21,
              position = position_nudge(x = 0.05)) +
  stat_pointinterval(data = emmeans_draws, 
                     aes(x = salinity_group, y = .value),
                     color = "black", fill = "grey", point_interval = "mean_qi", .width = 0.95, shape = 21,  stroke = 2, point_size = 4, alpha = 1,
                     position = position_nudge(x = -0.05)) +
  scale_fill_manual(values = c("#4B5320", "#000080")) +  # Custom fill colours
  scale_colour_manual(values = c("#4B5320", "#000080")) +
  theme_clean() +
  theme(legend.position = "none") +
  labs(
    subtitle = "",
    x = "Salinity group (ppt)",
    y = "Routine MO2 (mg O2 g/h)"
  )

fig_1

Figure 1a: Routine metabolic rate (i.e. MO2 (mg-1 O2 h-1) measured during SMR meassurments) plotted by salinity treatment. The small transparent points are the observed values, the shaded area behind the points is the a kernel density of the observed data, the large coloured point (to the right) is the observed mean, the large grey point with error bars (to the left) is the model estimated marginal mean (eemean) 95% Credible Intervals (95% CI).

10.2 SMR

10.2.1 Formating and scaling data

Here we are filtering the data frame to have only measure per fish for the SMR estimate

scale_list <- c("temp_mean", "mass", "cycles")

smr <- slope_tidy_smr %>%
    dplyr::group_by(id) %>%
    dplyr::reframe(temp_mean = mean(temp), mass = mass[1], SMR = SMR[1], salinity_group = salinity_group[1],
        cycles = length(order)) %>%
    dplyr::mutate(across(all_of(scale_list), ~scale(.x, center = TRUE, scale = FALSE),
        .names = "{.col}_z"))

10.2.2 Model structure

Here we will use a Bayesian Generalised Linear Mixed Model (GLMM) with a Gamma distribution and a log link Gamma(link = "log"), where the shape parameter (K) is also modelled as a function of the salinity group, shape ~ salinity_group. This models MO2 by salinity during the SMR phase to see if the fish held at different salinities have different SMRs. We have also added a few scaled predictors, that may help describe variation in the data, such as mass (g; 0.21–1.6) temperature (°C; 13.841–14.277), measurement order (1–28), and light/dark cycle (light or dark; light between 07:00:00 and 19:00:00), we also include a random effect for fish id to account for multiple MO2 measures on each fish (1 - 58). We allowed the the shape parameter (K) to vary as a function of some of the predictors (e.g. salinity_group, order_z) to improve fit.

smr_gamma_bf <- bf(SMR ~ temp_mean_z + cycles_z + mass_z + salinity_group, shape ~
    salinity_group, family = Gamma(link = "log"))

10.2.3 Prior selection

These are the default priors for the model. We will use these.

priors_default <- get_prior(smr_gamma_bf, data = smr, family = Gamma(link = "log"))
priors_default
##                    prior     class            coef group resp  dpar nlpar lb ub
##                   (flat)         b                                             
##                   (flat)         b        cycles_z                             
##                   (flat)         b          mass_z                             
##                   (flat)         b salinity_group9                             
##                   (flat)         b     temp_mean_z                             
##  student_t(3, -2.8, 2.5) Intercept                                             
##                   (flat)         b                            shape            
##                   (flat)         b salinity_group9            shape            
##     student_t(3, 0, 2.5) Intercept                            shape            
##        source
##       default
##  (vectorized)
##  (vectorized)
##  (vectorized)
##  (vectorized)
##       default
##       default
##  (vectorized)
##       default

These are custom generic standardised weakly informative priors, fit for all β estimates

priors_custom <- c(
  set_prior("normal(0, 1)", class = "b"),  # Priors for regression coefficients
  set_prior("student_t(3, -2.8, 2.5)", class = "Intercept"),  # Prior for the intercept
  set_prior("normal(0, 1)", class = "b", dpar = "shape"),  # Priors for shape parameter coefficients
  set_prior("student_t(3, 0, 2.5)", class = "Intercept", dpar = "shape")  # Prior for shape intercept
)
priors_custom
##                    prior     class coef group resp  dpar nlpar   lb   ub source
##             normal(0, 1)         b                             <NA> <NA>   user
##  student_t(3, -2.8, 2.5) Intercept                             <NA> <NA>   user
##             normal(0, 1)         b                 shape       <NA> <NA>   user
##     student_t(3, 0, 2.5) Intercept                 shape       <NA> <NA>   user

10.2.4 Run model

Here we run the model, I have hashed this out because I have saved the model for quick reloading.

## [1] "Model complete"

A prior only model

Here we reload the model

setwd(output_mods_wd)

smr_mod_gamma <- readRDS(file = "smr_mod_gamma.rds")
# smr_mod_gamma_priors <- readRDS(file = 'smr_mod_gamma_priors.rds')

10.2.5 Model diagnostics

Checking model convergence

color_scheme_set("red")
plot(smr_mod_gamma, ask = F)

Looking at prior and predicted distributions for our parameters

# keep_parameters <- c('b_Intercept', 'b_shape_Intercept', 'b_temp_mean_z',
# 'b_cycles_z', 'b_mass_z', 'b_salinity_group9', 'b_shape_salinity_group9',
# 'Intercept', 'Intercept_shape') prior_samples <-
# as_draws_df(smr_mod_gamma_priors) posterior_samples <-
# as_draws_df(smr_mod_gamma) posterior_samples$source <- 'Posterior'
# prior_samples$source <- 'Prior' combined_samples <-
# bind_rows(posterior_samples, prior_samples) # Convert to long format for
# ggplot long_samples <- pivot_longer(combined_samples, cols = -source,
# names_to = 'parameter', values_to = 'value') %>% dplyr::filter(parameter %in%
# keep_parameters) ggplot(long_samples, aes(x = value, fill = source)) +
# geom_density(alpha = 0.5) + # Overlay density plots facet_wrap(~parameter,
# scales = 'free') + # Separate by parameter labs(title = 'Prior vs Posterior
# Distributions', x = 'Parameter Value', y = 'Density') + theme_minimal()

Using leave one out (loo) measure of fit, the model appears to preform well, one Pareto k estimates falls outside the good range (0.7, 1]

loo(smr_mod_gamma)
## 
## Computed from 14000 by 58 log-likelihood matrix.
## 
##          Estimate   SE
## elpd_loo    135.9  9.2
## p_loo         9.0  3.3
## looic      -271.8 18.4
## ------
## MCSE of elpd_loo is NA.
## MCSE and ESS estimates assume MCMC draws (r_eff in [0.7, 0.9]).
## 
## Pareto k diagnostic values:
##                          Count Pct.    Min. ESS
## (-Inf, 0.7]   (good)     57    98.3%   183     
##    (0.7, 1]   (bad)       1     1.7%   <NA>    
##    (1, Inf)   (very bad)  0     0.0%   <NA>    
## See help('pareto-k-diagnostic') for details.

Model predictions generally align with the observed data, but there is a lot of uncertainty

color_scheme_set("red")
p <- pp_check(smr_mod_gamma, type = "dens_overlay")
p

10.2.6 Results

We did not see a meaningful difference between the routine metabolic rate for fish from the two salinity treatments.

10.2.6.1 Table 1

Table 1: Fixed effect Estimates (β) and 95% Credible Intervals (95% CI) from a Bayesian Generalised Linear Mixed Model (GLMM) with a Gamma distribution and a log link

model_est <- fixef(smr_mod_gamma, probs = c(0.025, 0.975)) %>%
    as.data.frame() %>%
    tibble::rownames_to_column(var = "Predictor") %>%
    dplyr::mutate(β = round(Estimate, 3), Q2.5 = round(Q2.5, 3), Q97.5 = round(Q97.5,
        3), `95% CI` = paste0("[", Q2.5, ", ", Q97.5, "]"))

model_est %>%
    dplyr::select(Predictor, "β", "95% CI") %>%
    gt()
Predictor β 95% CI
Intercept -2.741 [-2.873, -2.606]
shape_Intercept 2.063 [1.521, 2.558]
temp_mean_z -0.284 [-1.271, 0.713]
cycles_z -0.003 [-0.024, 0.019]
mass_z 1.321 [0.918, 1.73]
salinity_group9 -0.081 [-0.276, 0.116]
shape_salinity_group9 -0.013 [-0.787, 0.741]

Looking at the marginal mean difference between salinity groups

em_results <- emmeans(smr_mod_gamma, ~salinity_group)
contrast_results <- contrast(em_results, method = "pairwise")
em_results_df <- em_results %>%
    tidy() %>%
    mutate(across(where(is.numeric), ~exp(.)))
contrast_results_df <- contrast_results %>%
    tidy() %>%
    mutate(across(where(is.numeric), ~exp(.)))


em_results_df %>%
    gt()
salinity_group estimate lower.HPD upper.HPD
0 0.06448163 0.05667437 0.07396036
9 0.05935466 0.05204885 0.06851972

Pulling the emmeans draws for our plot

emmeans_draws <- smr_mod_gamma %>%
    emmeans(~salinity_group) %>%
    gather_emmeans_draws() %>%
    dplyr::mutate(.value = exp(.value), salinity_group = as.character(salinity_group))

emmeans_contrast_draws <- smr_mod_gamma %>%
    emmeans(~salinity_group) %>%
    contrast(method = "pairwise") %>%
    gather_emmeans_draws() %>%
    dplyr::mutate(.value = exp(.value))

10.2.6.2 Figure Si

This plot is in the main text of the manuscript as Figure 1b

mean_smr_salinity <- smr %>%
  dplyr::group_by(salinity_group) %>%
  dplyr::reframe(mean_SMR = mean(SMR, na.rm = TRUE))


fig_1b <- ggplot() +
  geom_violin(data = smr,
              aes(x = salinity_group, y = SMR, fill = salinity_group),
              color = NA, alpha = 0.2) +
  geom_jitter(data = smr,
              aes(x = salinity_group, y = SMR, fill = salinity_group),
              shape = 21, width = 0.3, size = 1, color = "black", alpha = 0.1) +
    geom_point(data = mean_smr_salinity,
             aes(x = salinity_group, y = mean_SMR, fill = salinity_group),
             size = 4, alpha = 1, stroke = 2, color = "black", shape = 21,
              position = position_nudge(x = 0.05)) +
  stat_pointinterval(data = emmeans_draws,
                     aes(x = salinity_group, y = .value),
                     color = "black", fill = "grey", point_interval = "mean_qi", .width = 0.95, shape = 21,  stroke = 2, point_size = 4, alpha = 1,
                     position = position_nudge(x = -0.05)) +
  scale_fill_manual(values = c("#4B5320", "#000080")) +  # Custom fill colours
  scale_colour_manual(values = c("#4B5320", "#000080")) +
  theme_clean() +
  theme(legend.position = "none") +
  labs(
    subtitle = "",
    x = "Salinity group (ppt)",
    y = "Standard metabolic rate (SMR; mg O2 g/h)"
  )

fig_1b

Figure 1b: The standard metabolic rate estimate (mg-1 O2 h-1) plotted by salinity treatment. The small transparent points are the observed values, the shaded area behind the points is the a kernel density of the observed data, the large coloured point (to the right) is the observed mean, the large grey point with error bars (to the left) is the model estimated marginal mean (eemean) 95% Credible Intervals (95% CI).

10.3 Incremental regression analyses

Here we are following the methods Urbina et al. (2012)[1] with an incremental regression analyses, in order to determine the best fit for the MO2 vs O2 data

This analysis approach evaluates the relative ‘fit’ of each polynomial order equation starting at zero and increasing to the third order, permitting a mathematical assessment of whether fish were oxyconforming or oxyregulatoring. If the data is best fitted/predicted by a single linear relationship (1st-order polynomial) with a positive slope, this would suggests the fish were oxyconforming. Alternately, if the relationship is best modelled by a flat regression (0th-order polynomial), or a higher order polynomial (2nd or 3rd-order polynomial) the fish is likely oxyregulatoring.

10.3.1 Building Bayesian regressions

Here we are using a Bayesian approach to model fitting with brm. These models take a long time to run, so I have saved them and re-loaded them to save time. I have also saved the summary data produced from the models, to save time, you can simply skip the hashed code and input the resulting summary data.

We will run our custom function, bayes_incremental_regression_by_id. This code takes a while to run. If you have already run this once, or have downloaded the saved models from GitHub skip this step (that’s why its hashed out), and run the next line, which loads the models.

# output_mods_bayes_wd <- paste0(output_mods_wd, './bayes-regs')
# ifelse(!dir.exists(output_mods_bayes_wd), dir.create(output_mods_bayes_wd),
# 'Folder already exists') ids <- slope_tidy %>% dplyr::distinct(id) %>%
# pull(id) plan(multisession) future_map( ids,
# bayes_incremental_regression_by_id, id_name = 'id', data = slope_tidy,
# response = 'MO2_g', predictor = 'DO', seed_number = 143019, save_models =
# TRUE, mod_output_wd = output_mods_bayes_wd ) plan(sequential)

Load all models and store in a list, will use a lot of memory. You can also skip this step and load the resulting data frames below. I am using the custom function load_rds, so we can compare them and generate predictions.

# bayes_reg_mods <- load_rds(model_dw = output_mods_bayes_wd)

10.3.2 Model fits

Get model fit parameters loo and r2 using the custom function, incremental_regression_bayes_fits.

# setwd(mod_data_wd) bayes_reg_mods_fit <-
# incremental_regression_bayes_fits(models = bayes_reg_mods)
# write.csv(bayes_reg_mods_fit, 'bayes_reg_mods_fit.csv', row.names = FALSE)

Reading in this model fit data frame, in the case you did not load in all the models.

setwd(mod_data_wd)
bayes_reg_mods_fit <- read.csv("bayes_reg_mods_fit.csv")

Selecting the best fitting model

elpd_loo, or the expected log pointwise predictive density for leave-one-out cross-validation, is a metric used in Bayesian model evaluation to assess the predictive accuracy of a model. The elpd_loo is an approximation of how well the model is expected to predict new data, based on leave-one-out cross-validation. Higher elpd_loo values indicate better predictive performance.

best_fit_bayes_reg <- bayes_reg_mods_fit %>%
    dplyr::group_by(id) %>%
    dplyr::mutate(elpd_loo_rank = rank(-elpd_loo)) %>%
    dplyr::select(id, model_type, elpd_loo, r2, elpd_loo_rank, r2_q2.5, r2_q97.5,
        estimate_DO, conf.low_DO, conf.high_DO)

10.3.3 Model predictions

Pulling our model predictions using a custom function bayes_mod_predictions.

# setwd(mod_data_wd) bayes_reg_mods_predictions <- bayes_mod_predictions(models
# = bayes_reg_mods, original_data = slope_tidy)
# write.csv(bayes_reg_mods_predictions, 'bayes_reg_mods_predictions.csv',
# row.names = FALSE)

Reading in the predicted data

setwd(mod_data_wd)
bayes_reg_mods_predictions <- read.csv("bayes_reg_mods_predictions.csv")

We are going to combined this with our best fitting model df, so we know how they ranks for LOO.

bayes_reg_mods_predictions <- full_join(bayes_reg_mods_predictions, best_fit_bayes_reg,
    by = c("id", "model_type"))

10.3.4 Model selection summary

The best fitting models were most often a 2nd-order polynomial (n = 22, 38%) or a 3rd-order polynomial (n = 16, 28%). This could suggest the presence of a critical oxygen threshold (Pcrit) where the relationship between O2 and MO2 changes. To confirm their is a Pcrit, we need to validated the shape of the polynomials and in should use a more specific model to test the Pcrit value. In any case, This type of model is indicative of oxyregulator.

The next most common are 0th-order and 1st-order polynomials (both n = 10, 17%). In the case of the 0th>-order model, it suggests that MO2 does not show a statistically significant dependence on the O2. In other words, the metabolic rate does not adjust based on oxygen availability, and there is no clear critical oxygen threshold (Pcrit) where the relationship changes. This is indicative of a oxyregulator. In the case of the 1st-order polynomials, it suggest the presences of linear relationship between o2 and MO2, which is indicative of oxyconformer. However, to be true evidence of a oxyconformer this relationship should be positive (i.e. as O2 falls MO2 also falls). Only 2 of the 10 individuals best modelled with a linear function had positive estimates with credible intervals that did not overlap with zero (Table Si).

best_mod <- best_fit_bayes_reg %>%
    dplyr::filter(elpd_loo_rank == 1)

total_fish <- nrow(best_mod)

table_bwm <- best_mod %>%
    dplyr::group_by(model_type) %>%
    dplyr::reframe(n = length(id), percent = round((n/total_fish) * 100, 2)) %>%
    dplyr::mutate(best_model_name = case_when(model_type == "lm_0" ~ "0th-order polynomial",
        model_type == "lm_1" ~ "1st-order polynomial", model_type == "lm_2" ~ "2nd-order polynomial",
        model_type == "lm_3" ~ "3rd-order polynomial", TRUE ~ "ERROR")) %>%
    dplyr::select(best_model_name, everything(), -model_type)


table_bwm %>%
    gt() %>%
    cols_align(align = "center", columns = everything())
best_model_name n percent
0th-order polynomial 10 17.24
1st-order polynomial 10 17.24
2nd-order polynomial 22 37.93
3rd-order polynomial 16 27.59

Summary of fish best model with a linear function.

table_lm_1 <- best_mod %>%
    dplyr::filter(model_type == "lm_1") %>%
    dplyr::mutate(r_sq_ci = paste0(round(r2, 3), " (", round(r2_q2.5, 3), "–",
        round(r2_q97.5, 3), ")"), est_ci = paste0(round(estimate_DO, 6), " (", round(conf.low_DO,
        6), "–", round(conf.high_DO, 6), ")"), conformer = if_else(conf.low_DO >
        0, "Conforming", "Not conforming")) %>%
    dplyr::select(id, r_sq_ci, est_ci, conformer) %>%
    dplyr::arrange(conformer) %>%
    dplyr::ungroup()


table_lm_1 %>%
    gt() %>%
    cols_align(align = "center", columns = everything()) %>%
    cols_label(id = "Fish ID", r_sq_ci = "r2 (CI)", est_ci = "Estimate (CI)", conformer = "Evidence of oxyconforming")
Fish ID r2 (CI) Estimate (CI) Evidence of oxyconforming
a_9_22nov_4 0.312 (0.094–0.491) 0.000849 (0.000422–0.001286) Conforming
d_9_25nov_3 0.197 (0.012–0.394) 0.001093 (0.000262–0.001907) Conforming
a_0_24nov_1 0.029 (0–0.134) 0.00017 (-0.000508–0.000862) Not conforming
a_0_24nov_3 0.058 (0–0.202) 0.000468 (-0.000291–0.001217) Not conforming
a_0_25nov_1 0.137 (0.002–0.331) -0.000358 (-0.000702–-1.5e-05) Not conforming
a_9_22nov_1 0.084 (0–0.266) 0.000324 (-0.000168–0.000814) Not conforming
b_0_25nov_2 0.108 (0.001–0.29) 0.000297 (-3e-05–0.000622) Not conforming
c_0_22nov_3 0.072 (0–0.256) 0.000531 (-0.000498–0.001572) Not conforming
c_9_27nov_2 0.068 (0–0.255) 0.000266 (-0.000304–0.000848) Not conforming
d_9_25nov_2 0.33 (0.102–0.507) -0.000891 (-0.001339–-0.000444) Not conforming

10.3.4.1 Ploting models

Now we are plotting each of the regressions. First making a directory to save the figures

Ploting all regression, and highlighting the model that has the best fit, based on AIC values

# Create a list to store the plots
plots <- list()
model_preds_list <- list()

for (id_i in ids) {
  
  # Filter data for the current ID
  df_i <- bayes_reg_mods_predictions %>%
    dplyr::filter(id == id_i) %>% 
    dplyr::mutate(line_size = if_else(elpd_loo_rank == 1, 2, 1),
           alpha_value = if_else(elpd_loo_rank == 1, 1, 0.4))
  
  x_min <- df_i %>%
    dplyr::reframe(min = min(DO), na.rm = TRUE) %>% 
    dplyr::pull(min)
  
  y_max <- df_i %>%
    dplyr::reframe(max = max(MO2_g), na.rm = TRUE) %>% 
    dplyr::pull(max)
  
  best_weighted_model_i <- best_fit_bayes_reg %>% 
    dplyr::filter(id == id_i & elpd_loo_rank == 1)
  
  poly_i_name <- best_weighted_model_i %>%
    dplyr::mutate(name = case_when(
      model_type == "lm_0" ~ "0th-order",
      model_type == "lm_1" ~ "1st-order",
      model_type == "lm_2" ~ "2nd-order",
      model_type == "lm_3" ~ "3rd-order",
      TRUE ~ "ERROR"
    )) %>% 
    dplyr::pull(name)
  
  r_i <- best_weighted_model_i %>% 
    dplyr::mutate(r_sq_ci = paste0(round(r2, 3), " (", 
                                    round(r2_q2.5, 3), "–", 
                                    round(r2_q97.5, 3), ")")) %>% 
    dplyr::pull(r_sq_ci)

  # Create the plot
  plot_i <- ggplot() +
    geom_ribbon(data = df_i,
                aes(x = DO, y = predicted, ymin = pred_lower, ymax = pred_upper, fill = model_type),
                alpha = 0.1) +
    geom_line(data = df_i, 
              aes(x = DO, y = predicted, colour = model_type, size = line_size, alpha = alpha_value)) +
    geom_point(data = df_i %>% dplyr::filter(elpd_loo_rank == 1), aes(x = DO, y = MO2_g), alpha = 0.6, colour = "black", size = 2) +
    scale_colour_manual(values = c("red", "blue", "green", "purple"), 
                        labels = c("0th Order", "1st Order", "2nd Order", "3rd Order")) +
    scale_size_identity() +  # Use the size values directly
    scale_alpha_identity(guide = "none") +  # Remove the alpha legend 
    annotate("text", x = x_min, 
             y = y_max, 
             label = paste0("Best fit: ",poly_i_name, "\n", "r2 = ", r_i), 
             hjust = 0, vjust = 1, size = 4) +
    labs(
      title = paste(id_i),
      x = "Dissolved oxygen percentage (DO)",
      y = "MO2 (o2 mg/g/h)",
      colour = "Model") +
    theme_classic() +
    theme(legend.position = "none")
  
  # Store the plot
  plots[[id_i]] <- plot_i
  
  print(plot_i)
}

#To save all plots to individual files
for (id_i in ids) {
  ggsave(filename = paste0(incremental_reg_bayes_wd, "./plot_", id_i, ".png"), plot = plots[[id_i]], width = 8, height = 6)
}

##!Note: need only best fitting model!

## [1] "Folder already exists"

Here we are grouping fish by best fitting model and getting an average trend. I have not finished this code yet

# best_fit <- bayes_reg_mods_predictions %>% dplyr::filter(elpd_loo_rank == 1)
# ids <- best_fit %>% dplyr::distinct(model_type) %>% pull(model_type)
# plan(multisession) future_map( ids, bayes_incremental_regression_by_id,
# id_name = 'model_type', data = best_fit, response = 'MO2_g', predictor =
# 'DO', save_models = TRUE, mod_output_wd = output_mods_bayes_global_wd )
# plan(sequential)
# bayes_reg_mods <- load_rds(model_dw = output_mods_bayes_global_wd)
# ggplot() + geom_ribbon(data = df_i, aes(x = DO, y = predicted, ymin =
# pred_lower, ymax = pred_upper, fill = model_type), alpha = 0.1) +
# geom_line(data = df_i, aes(x = DO, y = predicted, colour = model_type, size =
# line_size, alpha = alpha_value)) + geom_point(data =
# bayes_reg_mods_predictions %>% dplyr::filter(elpd_loo_rank == 1), aes(x = DO,
# y = MO2_g), alpha = 0.1, colour = 'black', size = 1) + geom_line(data =
# bayes_reg_mods_predictions %>% dplyr::filter(elpd_loo_rank == 1), aes(x = DO,
# y = predicted, by = id), alpha = 0.2) + scale_colour_manual(values = c('red',
# 'blue', 'green', 'purple'), labels = c('0th Order', '1st Order', '2nd Order',
# '3rd Order')) + annotate('text', x = x_min, y = y_max, label = paste0('Best
# fit: ',poly_i_name, '\n', 'r2 = ', r_i), hjust = 0, vjust = 1, size = 4) +
# facet_wrap(~model_type) + labs( title = paste('Model Fits vs Raw Data for
# ID', id_i), x = 'Dissolved oxygen percentage (DO)', y = 'MO2 (mg o2/g/h)',
# colour = 'Model') + theme_classic()
# global_models <- list( lm_0 = lmer(MO2_g ~ 1 + (1|id), data = slope_tidy %>%
# dplyr::filter(poly == 0), weights = weight_smr), lm_1 = lmer(MO2_g ~ DO +
# (1|id), data = slope_tidy %>% dplyr::filter(poly == 1), weights =
# weight_smr), lm_2 = lmer(MO2_g ~ poly(DO, 2) + (1|id), data = slope_tidy %>%
# dplyr::filter(poly == 2), weights = weight_smr), lm_3 = lmer(MO2_g ~ poly(DO,
# 3) + (1|id), data = slope_tidy %>% dplyr::filter(poly == 3), weights =
# weight_smr) ) global_predictions <- data.frame(DO = seq(min(slope_tidy$DO),
# max(slope_tidy$DO), length.out = 100)) for (model_name in
# names(global_models)) { predictions <- predict( global_models[[model_name]],
# newdata = global_predictions, re.form = NA, # Excludes random effects
# (population-level predictions) se.fit = TRUE # Returns standard errors )
# global_predictions[[paste0(model_name, '_fit')]] <- predictions$fit
# global_predictions[[paste0(model_name, '_lwr')]] <- predictions$fit - 1.96 *
# predictions$se.fit global_predictions[[paste0(model_name, '_upr')]] <-
# predictions$fit + 1.96 * predictions$se.fit } global_predictions_long <-
# global_predictions %>% pivot_longer( cols =
# matches('lm_.*_fit|lm_.*_lwr|lm_.*_upr'), names_to = c('model', '.value'),
# names_pattern = '(lm_\\d+)_(.*)' ) %>% dplyr::mutate(best_model_name =
# case_when( model == 'lm_0' ~ '0th-order polynomial', model == 'lm_1' ~
# '1st-order polynomial', model == 'lm_2' ~ '2nd-order polynomial', model ==
# 'lm_3' ~ '3rd-order polynomial', TRUE ~ 'ERROR' ))
# best_weighted_model_pred <- best_weighted_model %>% dplyr::left_join(.,
# model_preds_df, by = c('id', 'model')) %>% dplyr::ungroup() %>%
# dplyr::mutate(best_model_name = case_when( poly == 0 ~ '0th-order
# polynomial', poly == 1 ~ '1st-order polynomial', poly == 2 ~ '2nd-order
# polynomial', poly == 3 ~ '3rd-order polynomial', TRUE ~ 'ERROR' ))
# annotation_data <- table_bwm %>% dplyr::select(best_model_name, n) fig_1 <-
# ggplot() + geom_line(data = best_weighted_model_pred, aes(x = DO, y =
# MO2_pred, color = id), size = 1, alpha = 1) + geom_point(data = slope_tidy,
# aes(x = DO, y = MO2_g), alpha = 0.1, colour = 'black', size = 2) +
# geom_ribbon(data = global_predictions_long, aes(x = DO, ymin = lwr, ymax =
# upr, group = model), fill = '#FC6C85', alpha = 0.2) + # Shaded confidence
# intervals geom_line(data = global_predictions_long, aes(x = DO, y = fit),
# size = 1.5, color = '#FF007F') + facet_wrap(~best_model_name) +
# scale_color_grey(start = 0.1, end = 0.9) + labs( title = paste('Model
# estimates and observed data grouped by best fitting model'), x = 'Dissolved
# oxygen percentage (DO)', y = 'MO2 (O2 mg/g/h)') + theme_classic() +
# theme(legend.position = 'none') + geom_text(data = annotation_data, aes(x =
# -Inf, y = Inf, label = paste0('italic(n) == ', n)), hjust = -0.1, vjust =
# 1.2, inherit.aes = FALSE, parse = TRUE) fig_1

10.4 Pcrit Chabot method

For those fish that were best modelled with a 2nd or 3rd-order polynomial (n = 46) we will check to see if a Pcrit is present. We are filtering the data for only those fish.

higher_order <- c("lm_2", "lm_3")

check_pcrit_ids <- best_mod %>%
    dplyr::filter(model_type %in% higher_order) %>%
    dplyr::distinct(id) %>%
    pull(id)

check_pcrit_df <- slope_tidy %>%
    dplyr::filter(id %in% check_pcrit_ids)

We will calculate Pcrit using Chabot method and function calcO2crit. We are using our estimates for SMR (mean of lowest three).

This function uses the fifth percentile of the MO2 values observed at dissolved oxygen levels ≥ 80% air saturation as the criterion to assess low MO2 values. The algorithm then identifies all the MO2 measurements greater than this minimally acceptable MO2 value. Within this sub-set, it identifies the ̇ MO2 measurement made at the lowest DO and thereafter considers this DO as candidate for breakpoint (named pivotDO in the script). A regression is then calculated using observations at DO levels < pivotDO, and a first estimate of O2crit is calculated as the intersection of this regression line with the horizontal line representing SMR. The script then goes through validation steps to ensure that the slope of the regression is not so low that the line, projected to normoxic DO levels, passes below any MO2 values observed in normoxia. It also ensures that the intercept is not greater than zero. Corrective measures are taken if such problems are encountered.

lowestMO2 default is the quantile(MO2[DO >= 80], p=0.05). It is used to segment the data and locate the pivotDO.

ids <- check_pcrit_df %>%
    dplyr::distinct(id) %>%
    dplyr::pull()

pcrit_model_df_list <- list()
pcrit_models <- list()

for (id_i in ids) {

    df_i <- check_pcrit_df %>%
        dplyr::filter(id == id_i)

    o2crit <- calcO2crit(Data = df_i, SMR = df_i$SMR[1], lowestMO2 = NA, gapLimit = 4,
        max.nb.MO2.for.reg = 7)

    vaule <- o2crit$o2crit
    lowestMO2 = quantile(df_i$MO2[df_i$DO >= 80], p = 0.05)
    SMR <- o2crit$SMR
    nb_mo2_conforming <- o2crit$Nb_MO2_conforming
    r2 <- o2crit$r2
    method <- o2crit$Method
    p <- o2crit$P[1]

    pcrit_model_df <- tibble(id = id_i, pcrit_vaule = vaule, pcrit_smr = SMR, pcrit_lowestMO2 = lowestMO2,
        pcrit_nb_mo2_conforming = nb_mo2_conforming, pcrit_r2 = r2, pcrit_method = method,
        pcrit_p = p)

    pcrit_model_df_list[[id_i]] <- pcrit_model_df

    pcrit_models[[id_i]] <- o2crit

}

pcrit_model_df <- bind_rows(pcrit_model_df_list)

10.4.1 Ploting Pcrit

Here’s the plots for the Pcrit estimates

## png 
##   2

Printing in htlm document

ids <- check_pcrit_df %>%
    dplyr::distinct(id) %>%
    dplyr::pull()

for (id_i in ids) {

    comment <- check_pcrit_df %>%
        dplyr::filter(id == id_i) %>%
        dplyr::slice(1) %>%
        dplyr::mutate(comment = if_else(is.na(comments), "", paste0("#", comments))) %>%
        pull(comment)

    r2 <- pcrit_model_df %>%
        dplyr::filter(id == id_i) %>%
        dplyr::mutate(pcrit_r2 = round(pcrit_r2, 3)) %>%
        dplyr::pull(pcrit_r2)

    conforming <- pcrit_model_df %>%
        dplyr::filter(id == id_i) %>%
        dplyr::mutate(pcrit_nb_mo2_conforming = round(pcrit_nb_mo2_conforming, 3)) %>%
        dplyr::pull(pcrit_nb_mo2_conforming)

    P <- pcrit_model_df %>%
        dplyr::filter(id == id_i) %>%
        dplyr::mutate(pcrit_p = round(pcrit_p, 3)) %>%
        dplyr::pull(pcrit_p)

    SMR <- pcrit_model_df %>%
        dplyr::filter(id == id_i) %>%
        dplyr::mutate(pcrit_smr = round(pcrit_smr, 3)) %>%
        dplyr::pull(pcrit_smr)

    lowestMO2 <- pcrit_model_df %>%
        dplyr::filter(id == id_i) %>%
        dplyr::mutate(pcrit_lowestMO2 = round(pcrit_lowestMO2, 3)) %>%
        dplyr::pull(pcrit_lowestMO2)

    # Generate and render the plot
    plotO2crit(o2critobj = pcrit_models[[id_i]])

    # Add a title
    mtext(text = paste0(id_i, " ", comment), side = 3, line = 2, adj = 0, col = "blue",
        font = 2, cex = 1.2)

    mtext(text = paste0("R2 = ", r2, "; p = ", P, "; CP < SMR = ", conforming, "; SMR = ",
        SMR, "; lowestMO2 = ", lowestMO2), side = 3, line = 1, adj = 0, col = "blue",
        font = 0.5, cex = 0.8)
}

10.4.2 Pcrit rules

We need to set some rules as to when the Pcrit estimates are reliable, as it seems many of our fish do not reach a Pcrit.

We can filter for only cases were at the lowest O2 value three consecutive MO2 measures full below our SMR and fifth percentile of the MO2 values observed at dissolved O2 levels > 80%. In the model output these are called nb_mo2_conforming points. We can the visually inspect these to see if a Pcrit is present.

pcrit_list <- pcrit_model_df %>%
    dplyr::filter(pcrit_nb_mo2_conforming > 2) %>%
    pull(id)

paste0("Based on this rule there are ", length(pcrit_list), " fish with possible Pcrits.")
## [1] "Based on this rule there are 13 fish with possible Pcrits."

Here are the plots of these 13 fish for visual confirmation

for (id_i in pcrit_list) {

    comment <- check_pcrit_df %>%
        dplyr::filter(id == id_i) %>%
        dplyr::slice(1) %>%
        dplyr::mutate(comment = if_else(is.na(comments), "", paste0("#", comments))) %>%
        pull(comment)

    r2 <- pcrit_model_df %>%
        dplyr::filter(id == id_i) %>%
        dplyr::mutate(pcrit_r2 = round(pcrit_r2, 3)) %>%
        dplyr::pull(pcrit_r2)

    conforming <- pcrit_model_df %>%
        dplyr::filter(id == id_i) %>%
        dplyr::mutate(pcrit_nb_mo2_conforming = round(pcrit_nb_mo2_conforming, 3)) %>%
        dplyr::pull(pcrit_nb_mo2_conforming)

    P <- pcrit_model_df %>%
        dplyr::filter(id == id_i) %>%
        dplyr::mutate(pcrit_p = round(pcrit_p, 3)) %>%
        dplyr::pull(pcrit_p)

    SMR <- pcrit_model_df %>%
        dplyr::filter(id == id_i) %>%
        dplyr::mutate(pcrit_smr = round(pcrit_smr, 3)) %>%
        dplyr::pull(pcrit_smr)

    lowestMO2 <- pcrit_model_df %>%
        dplyr::filter(id == id_i) %>%
        dplyr::mutate(pcrit_lowestMO2 = round(pcrit_lowestMO2, 3)) %>%
        dplyr::pull(pcrit_lowestMO2)

    # Generate and render the plot
    plotO2crit(o2critobj = pcrit_models[[id_i]])

    # Add a title
    mtext(text = paste0(id_i, " ", comment), side = 3, line = 2, adj = 0, col = "blue",
        font = 2, cex = 1.2)

    mtext(text = paste0("R2 = ", r2, "; p = ", P, "; CP < SMR = ", conforming, "; SMR = ",
        SMR, "; lowestMO2 = ", lowestMO2), side = 3, line = 1, adj = 0, col = "blue",
        font = 1, cex = 0.8)
}

10.4.3 Pcrits numbers

Based on visual checks the following fish do have clear Pcrit values.

do_have_pcrit <- c("a_9_21nov_3", "b_0_24nov_1", "b_0_24nov_2", "b_0_25nov_1", "b_0_25nov_3",
    "b_0_26_nov_1", "b_9_21_nov_1", "b_9_21nov_2", "b_9_21nov_3", "d_0_21nov_3")

n_pcrit <- length(do_have_pcrit)

have_pcirt <- pcrit_model_df %>%
    dplyr::filter(id %in% do_have_pcrit)

mean_pcrit <- have_pcirt %>%
    dplyr::reframe(mean = mean(pcrit_vaule)) %>%
    pull(mean) %>%
    round(., 2)

min_pcrit <- have_pcirt %>%
    dplyr::reframe(min = min(pcrit_vaule)) %>%
    pull(min) %>%
    round(., 2)

max_pcrit <- have_pcirt %>%
    dplyr::reframe(max = max(pcrit_vaule)) %>%
    pull(max) %>%
    round(., 2)

print(paste0("There are ", n_pcrit, " fish with identified Pcrits and the mean Pcrit is ",
    mean_pcrit, " (range: ", min_pcrit, "–", max_pcrit, ")"))
## [1] "There are 10 fish with identified Pcrits and the mean Pcrit is 27.48 (range: 17.9–39.9)"

##!NOTE: Check mass and saility, add the visual assessment results

do_have_pcrit <- c("a_9_21nov_3", "b_0_24nov_1", "b_0_24nov_2", "b_0_25nov_1", "b_0_25nov_3",
    "b_0_26_nov_1", "b_9_21_nov_1", "b_9_21nov_2", "b_9_21nov_3", "d_0_21nov_3")

slope_tidy %>%
    dplyr::filter(id %in% do_have_pcrit) %>%
    dplyr::group_by(id) %>%
    dplyr::slice(1)
## # A tibble: 8 × 42
## # Groups:   id [8]
##    temp order phase cycle date                 time chamber_id    o2       mo2
##   <dbl> <dbl> <chr> <chr> <dttm>              <dbl> <chr>      <dbl>     <dbl>
## 1  14.1     6 smr   5     2024-11-21 00:00:00  21.2 a3          9.63 -0.000418
## 2  13.9     6 smr   5     2024-11-24 00:00:00  17.9 b1         10.4  -0.000443
## 3  13.9     6 smr   5     2024-11-24 00:00:00  17.9 b2         10.1  -0.000700
## 4  13.9     6 smr   5     2024-11-25 00:00:00  23.5 b1         10.3  -0.000321
## 5  13.9     6 smr   5     2024-11-25 00:00:00  23.5 b3         10.2  -0.000367
## 6  14.0     6 smr   5     2024-11-21 00:00:00  21.2 b2          9.70 -0.000411
## 7  14.0     6 smr   5     2024-11-21 00:00:00  21.2 b3          9.73 -0.000329
## 8  14.1     6 smr   5     2024-11-21 00:00:00  21.2 d3         10.1  -0.000525
## # ℹ 33 more variables: n <dbl>, bground <dbl>, leak <dbl>, mo2corr <dbl>,
## #   resp_cat_date <chr>, chamber_n <chr>, id_prox <chr>, time_hms <time>,
## #   date_chr <chr>, holding_tank <chr>, id <chr>, chamber_condition <chr>,
## #   chamber_type <chr>, mass <dbl>, length <dbl>, measured_salinity <dbl>,
## #   fasted <dbl>, comments <chr>, respirometer_group <chr>,
## #   salinity_group <chr>, start_date <chr>, chamber <chr>, volume <dbl>,
## #   light_dark <chr>, smr_chabot <dbl>, smr_chabot_method <chr>, DO <dbl>, …

11 References

[1] Claireaux, G. and Chabot, D. (2016) Responses by fishes to environmental hypoxia: integration through Fry’s concept of aerobic metabolic scope. Journal of Fish Biology https://doi.org/10.1111/jfb.12833

[2] Urbina MA, Glover CN, and Forster ME, (2012) A novel oxyconforming response in the freshwater fish Galaxias maculatus. Comparative Biochemistry and Physiology Part A: Molecular & Integrative Physiology. https://doi.org/10.1016/j.cbpa.2011.11.011

[3] Pick JL, Nakagawa S, and Noble DWA (2018) Reproducible, flexible and high-throughput data extraction from primary literature: The metaDigitise r package. https://doi.org/10.1111/2041-210X.13118

12 Session information

Here is a detailed list of the session information

sessioninfo::session_info()
## ─ Session info ───────────────────────────────────────────────────────────────
##  setting  value
##  version  R version 4.2.3 (2023-03-15 ucrt)
##  os       Windows 10 x64 (build 19044)
##  system   x86_64, mingw32
##  ui       RTerm
##  language (EN)
##  collate  English_Australia.utf8
##  ctype    English_Australia.utf8
##  tz       Australia/Sydney
##  date     2025-01-31
##  pandoc   3.1.1 @ C:/Program Files/RStudio/resources/app/bin/quarto/bin/tools/ (via rmarkdown)
## 
## ─ Packages ───────────────────────────────────────────────────────────────────
##  package           * version    date (UTC) lib source
##  abind               1.4-5      2016-07-21 [1] CRAN (R 4.2.0)
##  arrayhelpers        1.1-0      2020-02-04 [1] CRAN (R 4.2.3)
##  backports           1.4.1      2021-12-13 [1] CRAN (R 4.2.0)
##  bayesplot         * 1.11.1     2024-02-15 [1] CRAN (R 4.2.3)
##  betareg           * 3.2-0      2024-07-07 [1] CRAN (R 4.2.3)
##  bitops              1.0-7      2021-04-24 [1] CRAN (R 4.2.0)
##  boot                1.3-28.1   2022-11-22 [2] CRAN (R 4.2.3)
##  bridgesampling      1.1-2      2021-04-16 [1] CRAN (R 4.2.3)
##  brms              * 2.21.0     2024-03-20 [1] CRAN (R 4.2.3)
##  Brobdingnag         1.2-9      2022-10-19 [1] CRAN (R 4.2.3)
##  broom             * 1.0.4      2023-03-11 [1] CRAN (R 4.2.3)
##  broom.helpers       1.15.0     2024-04-05 [1] CRAN (R 4.2.3)
##  broom.mixed       * 0.2.9.5    2024-04-01 [1] CRAN (R 4.2.3)
##  bslib               0.7.0      2024-03-29 [1] CRAN (R 4.2.3)
##  cachem              1.0.7      2023-02-24 [1] CRAN (R 4.2.3)
##  car               * 3.1-2      2023-03-30 [1] CRAN (R 4.2.3)
##  carData           * 3.0-5      2022-01-06 [1] CRAN (R 4.2.3)
##  caTools             1.18.2     2021-03-28 [1] CRAN (R 4.2.3)
##  cellranger          1.1.0      2016-07-27 [1] CRAN (R 4.2.3)
##  checkmate           2.3.1      2023-12-04 [1] CRAN (R 4.2.3)
##  cli                 3.6.1      2023-03-23 [1] CRAN (R 4.2.3)
##  cluster             2.1.4      2022-08-22 [2] CRAN (R 4.2.3)
##  coda                0.19-4.1   2024-01-31 [1] CRAN (R 4.2.3)
##  codetools           0.2-19     2023-02-01 [2] CRAN (R 4.2.3)
##  colorspace          2.1-0      2023-01-23 [1] CRAN (R 4.2.3)
##  corrplot          * 0.92       2021-11-18 [1] CRAN (R 4.2.3)
##  curl                5.2.1      2024-03-01 [1] CRAN (R 4.2.3)
##  dagitty           * 0.3-4      2023-12-07 [1] CRAN (R 4.2.3)
##  data.table        * 1.14.8     2023-02-17 [1] CRAN (R 4.2.3)
##  datawizard        * 0.12.0     2024-07-11 [1] CRAN (R 4.2.3)
##  DEoptimR            1.1-3      2023-10-07 [1] CRAN (R 4.2.3)
##  devtools          * 2.4.5      2022-10-11 [1] CRAN (R 4.2.3)
##  digest              0.6.31     2022-12-11 [1] CRAN (R 4.2.3)
##  distributional      0.4.0      2024-02-07 [1] CRAN (R 4.2.3)
##  doParallel          1.0.17     2022-02-07 [1] CRAN (R 4.2.3)
##  dplyr             * 1.1.1      2023-03-22 [1] CRAN (R 4.2.3)
##  DT                * 0.33       2024-04-04 [1] CRAN (R 4.2.3)
##  ellipsis            0.3.2      2021-04-29 [1] CRAN (R 4.2.3)
##  emmeans           * 1.10.3     2024-07-01 [1] CRAN (R 4.2.3)
##  estimability        1.5.1      2024-05-12 [1] CRAN (R 4.2.3)
##  evaluate            0.24.0     2024-06-10 [1] CRAN (R 4.2.3)
##  fansi               1.0.4      2023-01-22 [1] CRAN (R 4.2.3)
##  farver              2.1.1      2022-07-06 [1] CRAN (R 4.2.3)
##  fastmap             1.1.1      2023-02-24 [1] CRAN (R 4.2.3)
##  flexmix             2.3-19     2023-03-16 [1] CRAN (R 4.2.3)
##  fontawesome         0.5.2      2023-08-19 [1] CRAN (R 4.2.3)
##  forcats           * 1.0.0      2023-01-29 [1] CRAN (R 4.2.3)
##  foreach             1.5.2      2022-02-02 [1] CRAN (R 4.2.3)
##  formatR             1.14       2023-01-17 [1] CRAN (R 4.2.3)
##  Formula             1.2-5      2023-02-24 [1] CRAN (R 4.2.2)
##  fs                  1.6.4      2024-04-25 [1] CRAN (R 4.2.3)
##  furrr             * 0.3.1      2022-08-15 [1] CRAN (R 4.2.3)
##  future            * 1.33.2     2024-03-26 [1] CRAN (R 4.2.3)
##  generics            0.1.3      2022-07-05 [1] CRAN (R 4.2.3)
##  ggdag             * 0.2.12     2024-03-08 [1] CRAN (R 4.2.3)
##  ggdist              3.3.2      2024-03-05 [1] CRAN (R 4.2.3)
##  ggExtra           * 0.10.0     2022-03-23 [1] CRAN (R 4.2.3)
##  ggfortify         * 0.4.16     2023-03-20 [1] CRAN (R 4.2.3)
##  gghalves          * 0.1.4      2022-11-20 [1] CRAN (R 4.2.3)
##  ggplot2           * 3.5.1      2024-04-23 [1] CRAN (R 4.2.3)
##  ggridges          * 0.5.6      2024-01-23 [1] CRAN (R 4.2.3)
##  ggthemes          * 4.2.4      2021-01-20 [1] CRAN (R 4.2.3)
##  globals             0.16.3     2024-03-08 [1] CRAN (R 4.2.3)
##  glue                1.6.2      2022-02-24 [1] CRAN (R 4.2.3)
##  gridExtra         * 2.3        2017-09-09 [1] CRAN (R 4.2.3)
##  gsw                 1.2-0      2024-08-19 [1] CRAN (R 4.2.3)
##  gt                * 0.11.0     2024-07-09 [1] CRAN (R 4.2.3)
##  gtable              0.3.5      2024-04-22 [1] CRAN (R 4.2.3)
##  gtExtras          * 0.4.5      2022-11-28 [1] CRAN (R 4.2.3)
##  gtsummary         * 1.7.2      2023-07-15 [1] CRAN (R 4.2.3)
##  highr               0.11       2024-05-26 [1] CRAN (R 4.2.3)
##  hms               * 1.1.3      2023-03-21 [1] CRAN (R 4.2.3)
##  htmltools           0.5.8.1    2024-04-04 [1] CRAN (R 4.2.3)
##  htmlwidgets         1.6.2      2023-03-17 [1] CRAN (R 4.2.3)
##  httpuv              1.6.9      2023-02-14 [1] CRAN (R 4.2.3)
##  httr                1.4.7      2023-08-15 [1] CRAN (R 4.2.3)
##  igraph            * 1.6.0      2023-12-11 [1] CRAN (R 4.2.3)
##  inline              0.3.19     2021-05-31 [1] CRAN (R 4.2.3)
##  insight             0.20.1     2024-06-11 [1] CRAN (R 4.2.3)
##  iterators           1.0.14     2022-02-05 [1] CRAN (R 4.2.3)
##  janitor           * 2.2.0      2023-02-02 [1] CRAN (R 4.2.3)
##  jquerylib           0.1.4      2021-04-26 [1] CRAN (R 4.2.3)
##  jsonlite            1.8.4      2022-12-06 [1] CRAN (R 4.2.3)
##  knitr               1.42       2023-01-25 [1] CRAN (R 4.2.3)
##  labeling            0.4.3      2023-08-29 [1] CRAN (R 4.2.3)
##  later               1.3.0      2021-08-18 [1] CRAN (R 4.2.3)
##  lattice           * 0.20-45    2021-09-22 [2] CRAN (R 4.2.3)
##  lazyeval            0.2.2      2019-03-15 [1] CRAN (R 4.2.3)
##  lifecycle           1.0.4      2023-11-07 [1] CRAN (R 4.2.3)
##  listenv             0.9.1      2024-01-29 [1] CRAN (R 4.2.3)
##  lme4              * 1.1-35.5   2024-07-03 [1] CRAN (R 4.2.3)
##  lmerTest          * 3.1-3      2020-10-23 [1] CRAN (R 4.2.3)
##  lmtest              0.9-40     2022-03-21 [1] CRAN (R 4.2.3)
##  loo                 2.8.0      2024-07-03 [1] CRAN (R 4.2.3)
##  lubridate         * 1.9.2      2023-02-10 [1] CRAN (R 4.2.3)
##  magrittr            2.0.3      2022-03-30 [1] CRAN (R 4.2.3)
##  marginaleffects   * 0.21.0     2024-06-14 [1] CRAN (R 4.2.3)
##  MASS                7.3-58.2   2023-01-23 [2] CRAN (R 4.2.3)
##  Matrix            * 1.5-3      2022-11-11 [2] CRAN (R 4.2.3)
##  matrixStats         1.3.0      2024-04-11 [1] CRAN (R 4.2.3)
##  mclust            * 6.1.1      2024-04-29 [1] CRAN (R 4.2.3)
##  memoise             2.0.1      2021-11-26 [1] CRAN (R 4.2.3)
##  mgcv                1.9-1      2023-12-21 [1] CRAN (R 4.2.3)
##  mime                0.12       2021-09-28 [1] CRAN (R 4.2.0)
##  miniUI              0.1.1.1    2018-05-18 [1] CRAN (R 4.2.3)
##  minqa               1.2.7      2024-05-20 [1] CRAN (R 4.2.3)
##  modeltools          0.2-23     2020-03-05 [1] CRAN (R 4.2.0)
##  multcomp            1.4-25     2023-06-20 [1] CRAN (R 4.2.3)
##  munsell             0.5.1      2024-04-01 [1] CRAN (R 4.2.3)
##  mvtnorm             1.2-5      2024-05-21 [1] CRAN (R 4.2.3)
##  nlme                3.1-162    2023-01-31 [2] CRAN (R 4.2.3)
##  nloptr              2.1.1      2024-06-25 [1] CRAN (R 4.2.3)
##  nnet                7.3-18     2022-09-28 [2] CRAN (R 4.2.3)
##  numDeriv            2016.8-1.1 2019-06-06 [1] CRAN (R 4.2.0)
##  oce                 1.8-3      2024-08-17 [1] CRAN (R 4.2.3)
##  opdisDownsampling   1.0.1      2024-04-15 [1] CRAN (R 4.2.3)
##  paletteer           1.6.0      2024-01-21 [1] CRAN (R 4.2.3)
##  parallelly          1.37.1     2024-02-29 [1] CRAN (R 4.2.3)
##  pbmcapply           1.5.1      2022-04-28 [1] CRAN (R 4.2.0)
##  performance       * 0.12.0     2024-06-08 [1] CRAN (R 4.2.3)
##  permute           * 0.9-7      2022-01-27 [1] CRAN (R 4.2.3)
##  pillar              1.9.0      2023-03-22 [1] CRAN (R 4.2.3)
##  pkgbuild            1.4.4      2024-03-17 [1] CRAN (R 4.2.3)
##  pkgconfig           2.0.3      2019-09-22 [1] CRAN (R 4.2.3)
##  pkgload             1.3.2      2022-11-16 [1] CRAN (R 4.2.3)
##  plotly            * 4.10.1     2022-11-07 [1] CRAN (R 4.2.3)
##  plyr                1.8.8      2022-11-11 [1] CRAN (R 4.2.3)
##  posterior           1.6.0      2024-07-03 [1] CRAN (R 4.2.3)
##  pracma              2.4.4      2023-11-10 [1] CRAN (R 4.2.3)
##  profvis             0.3.8      2023-05-02 [1] CRAN (R 4.2.3)
##  promises            1.2.0.1    2021-02-11 [1] CRAN (R 4.2.3)
##  purrr             * 1.0.1      2023-01-10 [1] CRAN (R 4.2.3)
##  qqconf              1.3.2      2023-04-14 [1] CRAN (R 4.2.3)
##  qqplotr           * 0.0.6      2023-01-25 [1] CRAN (R 4.2.3)
##  QuickJSR            1.3.0      2024-07-08 [1] CRAN (R 4.2.3)
##  R6                  2.5.1      2021-08-19 [1] CRAN (R 4.2.3)
##  ragg                1.2.5      2023-01-12 [1] CRAN (R 4.2.3)
##  RColorBrewer      * 1.1-3      2022-04-03 [1] CRAN (R 4.2.0)
##  Rcpp              * 1.0.10     2023-01-22 [1] CRAN (R 4.2.3)
##  RcppParallel        5.1.8      2024-07-06 [1] CRAN (R 4.2.3)
##  readr             * 2.1.4      2023-02-10 [1] CRAN (R 4.2.3)
##  readxl            * 1.4.2      2023-02-09 [1] CRAN (R 4.2.3)
##  rematch2            2.1.2      2020-05-01 [1] CRAN (R 4.2.3)
##  remotes             2.5.0      2024-03-17 [1] CRAN (R 4.2.3)
##  reshape2            1.4.4      2020-04-09 [1] CRAN (R 4.2.3)
##  respirometry      * 2.0.0      2024-07-18 [1] CRAN (R 4.2.3)
##  rlang               1.1.0      2023-03-14 [1] CRAN (R 4.2.3)
##  rmarkdown           2.21       2023-03-26 [1] CRAN (R 4.2.3)
##  robustbase          0.99-4-1   2024-09-27 [1] CRAN (R 4.2.3)
##  rstan             * 2.32.6     2024-03-05 [1] CRAN (R 4.2.3)
##  rstantools          2.4.0      2024-01-31 [1] CRAN (R 4.2.3)
##  rstudioapi          0.14       2022-08-22 [1] CRAN (R 4.2.3)
##  sandwich            3.1-0      2023-12-11 [1] CRAN (R 4.2.3)
##  sass                0.4.9      2024-03-15 [1] CRAN (R 4.2.3)
##  scales              1.3.0      2023-11-28 [1] CRAN (R 4.2.3)
##  sessioninfo       * 1.2.2      2021-12-06 [1] CRAN (R 4.2.3)
##  shiny             * 1.8.1.1    2024-04-02 [1] CRAN (R 4.2.3)
##  shinybusy         * 0.3.3      2024-03-09 [1] CRAN (R 4.2.3)
##  shinycssloaders   * 1.0.0      2020-07-28 [1] CRAN (R 4.2.3)
##  snakecase           0.11.1     2023-08-27 [1] CRAN (R 4.2.3)
##  SRS               * 0.2.3      2022-03-27 [1] CRAN (R 4.2.3)
##  StanHeaders       * 2.32.9     2024-05-29 [1] CRAN (R 4.2.3)
##  stringi             1.7.12     2023-01-11 [1] CRAN (R 4.2.2)
##  stringr           * 1.5.0      2022-12-02 [1] CRAN (R 4.2.3)
##  survival            3.5-3      2023-02-12 [2] CRAN (R 4.2.3)
##  svUnit              1.0.6      2021-04-19 [1] CRAN (R 4.2.3)
##  systemfonts         1.0.4      2022-02-11 [1] CRAN (R 4.2.3)
##  tensorA             0.36.2.1   2023-12-13 [1] CRAN (R 4.2.3)
##  textshaping         0.3.6      2021-10-13 [1] CRAN (R 4.2.3)
##  TH.data             1.1-2      2023-04-17 [1] CRAN (R 4.2.3)
##  tibble            * 3.2.1      2023-03-20 [1] CRAN (R 4.2.3)
##  tidybayes         * 3.0.6      2023-08-12 [1] CRAN (R 4.2.3)
##  tidygraph           1.3.0      2023-12-18 [1] CRAN (R 4.2.3)
##  tidyr             * 1.3.0      2023-01-24 [1] CRAN (R 4.2.3)
##  tidyselect          1.2.1      2024-03-11 [1] CRAN (R 4.2.3)
##  tidyverse         * 2.0.0      2023-02-22 [1] CRAN (R 4.2.3)
##  timechange          0.2.0      2023-01-11 [1] CRAN (R 4.2.3)
##  twosamples          2.0.1      2023-06-23 [1] CRAN (R 4.2.3)
##  tzdb                0.3.0      2022-03-28 [1] CRAN (R 4.2.3)
##  urlchecker          1.0.1      2021-11-30 [1] CRAN (R 4.2.3)
##  usethis           * 2.2.3      2024-02-19 [1] CRAN (R 4.2.3)
##  utf8                1.2.3      2023-01-31 [1] CRAN (R 4.2.3)
##  V8                  4.4.2      2024-02-15 [1] CRAN (R 4.2.3)
##  vctrs               0.6.1      2023-03-22 [1] CRAN (R 4.2.3)
##  vegan             * 2.6-6.1    2024-05-21 [1] CRAN (R 4.2.3)
##  viridisLite         0.4.2      2023-05-02 [1] CRAN (R 4.2.3)
##  withr               3.0.0      2024-01-16 [1] CRAN (R 4.2.3)
##  xfun                0.38       2023-03-24 [1] CRAN (R 4.2.3)
##  xml2                1.3.3      2021-11-30 [1] CRAN (R 4.2.3)
##  xtable              1.8-4      2019-04-21 [1] CRAN (R 4.2.3)
##  yaml                2.3.7      2023-01-23 [1] CRAN (R 4.2.3)
##  zoo                 1.8-12     2023-04-13 [1] CRAN (R 4.2.3)
## 
##  [1] C:/R
##  [2] C:/Program Files/R/R-4.2.3/library
## 
## ──────────────────────────────────────────────────────────────────────────────
LS0tDQp0aXRsZTogImdtYWMtbGFiLWNoYXJ0Ig0KYXV0aG9yOiAiSmFrZSBNYXJ0aW4iDQpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiAlWScpYCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgZGVwdGg6IDQNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRoZW1lOiAgY29zbW8NCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogIHBkZl9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0Ka25pdDogfA0KICAoZnVuY3Rpb24oaW5wdXQsIC4uLikgew0KICAgIHJtYXJrZG93bjo6cmVuZGVyKA0KICAgICAgaW5wdXQsDQogICAgICBvdXRwdXRfZmlsZSA9IHBhc3RlMCgNCiAgICAgICAnaW5kZXguaHRtbCcNCiAgICAgICksDQogICAgICBlbnZpciA9IGdsb2JhbGVudigpDQogICAgKQ0KICB9KQ0KLS0tDQoNCjwhLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLT4NCiMgUkVBRCBNRSANCjwhLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLT4NCg0KKipTVU1NQVJZKiogIA0KVGhpcyBSIGNvZGUgaXMgdXNlZCB0byBlc3RpbWF0ZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gb3h5Z2VuIGNvbnN1bXB0aW9uIChNT+KCgikgYW5kIGFtYmllbnQgb3h5Z2VuIHBhcnRpYWwgcHJlc3N1cmUgKFBP4oKCKSBpbiB0aGUgQ29tbW9uIEdhbGF4aWFzICgqR2FsYXhpYXMgbWFjdWxhdHVzKikuIEl0IGFsc28gZXN0aW1hdGVzIHRoZSBjcml0aWNhbCBwYXJ0aWFsIHByZXNzdXJlIG9mIG94eWdlbiBmb3IgYWVyb2JpYyBtZXRhYm9saXNtIChQY3JpdCksIHdoaWNoIGlzIGNvbW1vbmx5IHVuZGVyc3Rvb2QgYXMgdGhlIHRocmVzaG9sZCBiZWxvdyB3aGljaCB0aGUgb3h5Z2VuIGNvbnN1bXB0aW9uIHJhdGUgY2FuIG5vIGxvbmdlciBiZSBzdXN0YWluZWQuIFRoZSBhc3NvY2lhdGVkIGFydGljbGUgaXMgIlRoZSByb2xlIG9mIG9zbW9yZXNwaXJhdG9yeSBjb21wcm9taXNlIGluIGh5cG94aWEgdG9sZXJhbmNlIG9mIHRoZSBwdXJwb3J0ZWRseSBveHljb25mb3JtaW5nIHRlbGVvc3QgKkdhbGF4aWFzIG1hY3VsYXR1cyouIiBJZiB5b3UgY2xpY2sgdGhlICJDb2RlIiBidXR0b24gaW4gdGhlIHRvcCByaWdodCBvZiB0aGUgSFRNTCBkb2N1bWVudCwgeW91IGNhbiBkb3dubG9hZCB0aGUgUm1kIGZpbGUuICANCg0KKipBSU0qKiAgDQpUaGUgYXJ0aWNsZSBhaW1zIHRvIHRlc3Qgd2hldGhlciBHYWxheGlhcyBtYWN1bGF0dXMgY2FuIG1haW50YWluIG94eWdlbiBjb25zdW1wdGlvbiAoTU/igoIpIGFzIGFtYmllbnQgUE/igoIgZmFsbHMgYW5kLCBpZiBzbywgYXQgd2hhdCBsZXZlbCBpdCByZWFjaGVzIHRoZSBjcml0aWNhbCBwYXJ0aWFsIHByZXNzdXJlIG9mIG94eWdlbiBmb3IgYWVyb2JpYyBtZXRhYm9saXNtIChQY3JpdCkuICANCg0KKipBVVRIT1JTKiogIA0KVG8gYmUgYWRkZWQgIA0KDQoqKkFGRklMSUFUSU9OUyoqICANClRvIGJlIGFkZGVkICANCg0KKipBSU0qKiAgDQpUbyBiZSBhZGRlZCAgDQoNCioqRGlzY2xhaW1lcioqICANCkkgKEpha2UgTWFydGluKSBhbSBkeXNsZXhpYy4gSSBoYXZlIG1hZGUgYW4gZWZmb3J0IHRvIHJldmlldyB0aGUgc2NyaXB0IGZvciBzcGVsbGluZyBlcnJvcnMsIGJ1dCBzb21lIHdpbGwgbGlrZWx5IHJlbWFpbi4gSSBhcG9sb2dpc2UuIFBsZWFzZSByZWFjaCBvdXQgdmlhIHRoZSBjb250YWN0IGRldGFpbHMgYmVsb3cgaWYgYW55dGhpbmcgaXMgdW5jbGVhci4gIA0KDQo8IS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0+DQojIEtuaXQgc2V0dGluZ3MgDQo8IS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0+DQoNClRoZXNlIGFyZSB0aGUgc2V0dGluZ3MgZm9yIHRoZSBodG1sIG91dHB1dC4gV2Ugd2lsbCB1c2UgdGhlIGh0bWwgdG8gbWFrZSBvdXQgaW5kZXggZmlsZSBvbiBHaXQNCg0KYGBge3Igc2V0dXB9DQoja25pdGVyIHNlZXR0aW5nDQprbml0cjo6b3B0c19jaHVuayRzZXQoDQptZXNzYWdlID0gRkFMU0UsDQp3YXJuaW5nID0gRkFMU0UsICMgbm8gd2FybmluZ3MNCmNhY2hlID0gVFJVRSwjIENhY2hlaW5nIHRvIHNhdmUgdGltZSB3aGVuIGtuaXRpbmcNCnRpZHkgPSBUUlVFDQopDQpgYGANCg0KPCEtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tPg0KIyBTY3JpcHQgY29udGFjdA0KPCEtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tPg0KDQoqKkpha2UgTS4gTWFydGluKiogICANCg0KKipFbWFpbCoqOiBqYWtlLm1hcnRpbkBkZWFraW4uZWR1LmF1IChvciBqYWtlLm1hcnRpbi5yZXNlYXJjaEBnbWFpbC5jb20pICAgDQoNCioqV2ViKio6ICBodHRwczovL2pha2UubWFydGluLm9yZyAgIA0KDQoqKkdpdEh1YioqOiBodHRwczovL2dpdGh1Yi5jb20vSmFrZU1hcnRpblJlc2VhcmNoICAgDQoNCjwhLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLT4NCiMgUmVxdWlyZWQgcGFja2FnZXMNCjwhLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLT4NCg0KVGhlc2UgYXJlIHRoZSBSIHBhY2thZ2VzIHJlcXVpcmVkIGZvciB0aGlzIHNjcmlwdC4gWW91IHdpbGwgbmVlZCB0byBpbnN0YWxsIGEgcGFja2FnZSBjYWxsZWQgcGFjbWFuIHRvIHJ1biB0aGUgcF9sb2FkIGZ1bmN0aW9uLiAgIA0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgcmVzdWx0cz0naGlkZSd9DQojIHRoaXMgaW5zdGFsbHMgYW5kIGxvYWQgcGFja2FnZXMNCiMgbmVlZCB0byBpbnN0YWxsIHBhY21hbg0KcGFjbWFuOjpwX2xvYWQoImdncGxvdDIiLCANCiAgICAgICAgICAgICAgICJnZ3RoZW1lcyIsIA0KICAgICAgICAgICAgICAgImdnZm9ydGlmeSIsIA0KICAgICAgICAgICAgICAgImd0RXh0cmFzIiwgDQogICAgICAgICAgICAgICAiaWdyYXBoIiwNCiAgICAgICAgICAgICAgICJkYWdpdHR5IiwNCiAgICAgICAgICAgICAgICJnZ2RhZyIsDQogICAgICAgICAgICAgICAiZ2dyaWRnZXMiLA0KICAgICAgICAgICAgICAgImdnaGFsdmVzIiwNCiAgICAgICAgICAgICAgICJnZ0V4dHJhIiwNCiAgICAgICAgICAgICAgICJncmlkRXh0cmEiLA0KICAgICAgICAgICAgICAgImNvcnJwbG90IiwNCiAgICAgICAgICAgICAgICJSQ29sb3JCcmV3ZXIiLCANCiAgICAgICAgICAgICAgICJndCIsIA0KICAgICAgICAgICAgICAgImd0c3VtbWFyeSIsDQogICAgICAgICAgICAgICAiZ3JpZCIsDQogICAgICAgICAgICAgICAicGxvdGx5IiwNCiAgICAgICAgICAgICAgICJiYXllc3Bsb3QiLCAjIGRhdGEgdmlzdWFsaXNhdGlvbg0KICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgInRpZHl2ZXJzZSIsIA0KICAgICAgICAgICAgICAgImphbml0b3IiLCANCiAgICAgICAgICAgICAgICJyZWFkeGwiLCANCiAgICAgICAgICAgICAgICJicm9vbS5taXhlZCIsIA0KICAgICAgICAgICAgICAgImRhdGEudGFibGUiLCANCiAgICAgICAgICAgICAgICJkZXZ0b29scyIsDQogICAgICAgICAgICAgICAiaG1zIiwgIyBkYXRhIHRpZHkNCiAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgIm1hcmdpbmFsZWZmZWN0cyIsIA0KICAgICAgICAgICAgICAgImJybXMiLCANCiAgICAgICAgICAgICAgICJyc3RhbiIsIA0KICAgICAgICAgICAgICAgInBlcmZvcm1hbmNlIiwgDQogICAgICAgICAgICAgICAiZW1tZWFucyIsIA0KICAgICAgICAgICAgICAgInRpZHliYXllcyIsIA0KICAgICAgICAgICAgICAgInZlZ2FuIiwNCiAgICAgICAgICAgICAgICJiZXRhcmVnIiwNCiAgICAgICAgICAgICAgICJsbWU0IiwgDQogICAgICAgICAgICAgICAiY2FyIiwgDQogICAgICAgICAgICAgICAibG1lclRlc3QiLA0KICAgICAgICAgICAgICAgInFxcGxvdHIiLA0KICAgICAgICAgICAgICAgInJlc3Bpcm9tZXRyeSIsDQogICAgICAgICAgICAgICAibWNsdXN0IiwNCiAgICAgICAgICAgICAgICJmdXJyciIsICMgbW9kZWxsaW5nIA0KICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgImRhdGF3aXphcmQiLCANCiAgICAgICAgICAgICAgICJTUlMiLCAjIGRhdGEgbWFuaXB1bGF0aW9uIA0KICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAic2Vzc2lvbmluZm8iICMgcmVwcm9kdWNpYmlsaXR5DQogICAgICAgICAgICAgICAgICAgICAgICkNCmBgYA0KDQoNCjwhLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLT4NCiMgRnVuY3Rpb25zIChjdXN0b20pDQo8IS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0+DQoNCkhlcmUgYXJlIHNvbWUgY3VzdG9tIGZ1bmN0aW9uIHVzZWQgd2l0aGluIHRoaXMgc2NyaXB0LiAgICANCg0KYGJheWVzX2luY3JlbWVudGFsX3JlZ3Jlc3Npb25fYnlfaWQoKWA6IEEgY3VzdG9tIGZ1bmN0aW9uIHRvIGJ1aWxkIEJheWVzaWFuIGluY3JlbWVudGFsIHJlZ3Jlc3Npb25zLiBJdCBpcyBmb3JtYXR0ZWQgZm9yIHJ1bm5pbmcgYSBsaXN0IG9mIHN1YiBncm91cCBtb2RlbHMgKGlkcykgaW4gcGFyYWxsZWwgd2l0aCA0IGNvcmVzLiBJdCB1c2VzIGBicm0oKWAgd2l0aCBhIEdhdXNzaWFuIGVycm9yIGZ1bmN0aW9uLiAgIA0KDQoqKlVzZSoqOiBUaGUgZnVuY3Rpb24gdGFrZXMgYW4gZ3JvdXBpbmcgZmFjdG9yL2lkIHRoYXQgd2lsbCBiZSB1c2VkIHRvIGZpbHRlciB0aGUgZGF0YSBmb3IgdGhlIHJlZ3Jlc3Npb24sIGlmIG5vbmUgaXMgcHJvdmVkIGl0IHdpbGwgdXNlIGFsbCBkYXRhIChpZF9pKSwgdGhlIGNvbHVtbiBuYW1lIG9mIHRoZSBncm91cGluZyBmYWN0b3IvY2hhcnRlciBpbiB0aGUgZGF0YSBmcmFtZSwgaWYgbm9uZSBpcyBwcm92ZWQgaXQgd2lsbCB1c2UgYWxsIGRhdGEgKGlkX25hbWUpLCB0aGUgZGF0YSBmcmFtZSAoZGF0YSksIHRoZSBwcmVkaWN0b3Igb2YgaW50ZXJlc3QgKHByZWRpY3RvciksIHRoZSByZXNwb25zZSBvZiBpbnRlcmVzdCAocmVzcG9uc2UpLCB0aGUgcmFuZG9tIHNlZWQgbnVtYmVyIGZvciBtb2RlbCByZXByb2R1Y2liaWxpdHkgKHNlZWRfbnVtYmVyKSwgd2hlcmUgeW91IHdvdWxkIGxpa2UgdG8gc2F2ZSB0aGUgbW9kZWwgb3V0cHV0cyAoc2F2ZV9tb2RlbHMgPSBsb2dpY2FsIGFyZ3VtZW50KSwgdGhlIG91dHB1dCBkaXJlY3RvcnkgZm9yIHRoZSBzYXZlZCByZHMgbW9kZWxzIChtb2Rfb3V0cHV0X3dkKSAgIA0KDQpiYXllc19pbmNyZW1lbnRhbF9yZWdyZXNzaW9uX2J5X2lkKGlkX2kgPSAieW91cl9pZCIsIGlkX25hbWUgPSAiY29sdW1uIG5hbWUgZm9yIGlkcyIsIGRhdGEgPSAieW91ciBkYXRhIiwgcHJlZGljdG9yID0gInByZWRpY3RvciB2YXJpYWJsZSIsIHJlc3BvbnNlID0gInJlc3BvbnNlIHZhcmlhYmxlIiwgc2VlZF9udW1iZXIgPSBpbnRlZ2VyLCBzYXZlX21vZGVscyA9IFRSVUUvRkFMU0UsIG1vZF9vdXRwdXRfd2QgPSAiZGlyZWN0b3J5IGZvciBtb2RlbCBvdXRwdXQiKQ0KDQpgYGB7cn0NCiMgSW5zdGVhZCB3ZSBjb3VsZCBhbHNvIHVzZSBhIGRpc3RyaWJ1dGlvbmFsIHJlZ3Jlc3Npb24gYXBwcm9hY2gsIGJ5IHNwZWNpZmljYWxseSBtb2RlbGxpbmcgdGhlIHZhcmlhbmNlIGJ5IERPIChlLmcuIHNpZ21hIH4gRE8pLiBXZWlnaHRpbmcgbWF5IG5vdCBiZSByZXF1aXJlZCBpbiB0aGlzIGNhc2UsIEkgZG9uJ3QgdGhpbmsgaGlnaGVyIGRlbnNpdHkgb2YgdmF1bGVzIGluIGEgZ2l2ZW4gc3BhY2Ugd2lsbCBlZmZlY3QgQmF5ZXNpYW4gZXN0aW1hdGVzIGxpa2UgaXQgZG9lcyBpbiBmcmVxdWVudGlzdCBtb2RlbHMuIFNlZSBkaXNjb3Vyc2UgaHR0cHM6Ly9kaXNjb3Vyc2UubWMtc3Rhbi5vcmcvdC93ZWlnaHRzLWluLWJybS80Mjc4ICAgDQoNCmJheWVzX2luY3JlbWVudGFsX3JlZ3Jlc3Npb25fYnlfaWQgPC0gZnVuY3Rpb24oaWRfaSwgaWRfbmFtZSwgZGF0YSwgcHJlZGljdG9yLCByZXNwb25zZSwgc2VlZF9udW1iZXIsIHNhdmVfbW9kZWxzLCBtb2Rfb3V0cHV0X3dkKSB7DQogICMgSW5pdGlhdGUgYW4gZW1wdHkgbGlzdCB0byBzdG9yZSBtb2RlbHMNCiAgbW9kZWxzIDwtIGxpc3QoKQ0KICANCiAgIyBDaGVjayBpZiBpZF9uYW1lIGlzIG1pc3NpbmcsIE5VTEwsIG9yIGJsYW5rLCBhbmQgYXNzaWduIE5BIGlmIHNvDQogIGlmIChtaXNzaW5nKGlkX25hbWUpIHx8IGlzLm51bGwoaWRfbmFtZSkgfHwgaWRfbmFtZSA9PSAiIikgew0KICAgIGlkX25hbWUgPC0gTkENCiAgfQ0KICANCiAgIyBDaGVjayBpZiBpZF9pIGlzIG1pc3NpbmcsIE5VTEwsIG9yIGJsYW5rLCBhbmQgYXNzaWduIE5BIGlmIHNvDQogIGlmIChtaXNzaW5nKGlkX2kpIHx8IGlzLm51bGwoaWRfaSkgfHwgaWRfaSA9PSAiIikgew0KICAgIGlkX25hbWUgPC0gTkENCiAgfQ0KICANCiAgIyBGaWx0ZXIgZGF0YSBmb3IgdGhlIGN1cnJlbnQgSUQgaWYgaWRfbmFtZSBpcyBnaXZlbiBhcyBhIGZhY3RvciBvciBjaGFyYWN0ZXIgYW5kIGlkX2kgaXMgZGVmaW5lZA0KICBkZl9pIDwtIGRhdGEgJT4lDQogICAgZHBseXI6OmZpbHRlcigNCiAgICAgIGlmICghaXMubmEoaWRfaSkgJiYgKGlzLmZhY3RvcihkYXRhW1tpZF9uYW1lXV0pIHx8IGlzLmNoYXJhY3RlcihkYXRhW1tpZF9uYW1lXV0pKSkgew0KICAgICAgICAhIXJsYW5nOjpzeW0oaWRfbmFtZSkgPT0gaWRfaQ0KICAgICAgfSBlbHNlIHsNCiAgICAgICAgVFJVRQ0KICAgICAgfQ0KICAgICkNCiAgDQogICMgRHluYW1pY2FsbHkgY3JlYXRlIGZvcm11bGFzDQogIGZvcm11bGFfbG1fMCA8LSByZWZvcm11bGF0ZSgiMSIsIHJlc3BvbnNlKQ0KICBmb3JtdWxhX2xtXzEgPC0gcmVmb3JtdWxhdGUocHJlZGljdG9yLCByZXNwb25zZSkNCiAgZm9ybXVsYV9sbV8yIDwtIHJlZm9ybXVsYXRlKHNwcmludGYoInBvbHkoJXMsIDIpIiwgcHJlZGljdG9yKSwgcmVzcG9uc2UpDQogIGZvcm11bGFfbG1fMyA8LSByZWZvcm11bGF0ZShzcHJpbnRmKCJwb2x5KCVzLCAzKSIsIHByZWRpY3RvciksIHJlc3BvbnNlKQ0KICANCiAgIyBGaXQgYW5kIHN0b3JlIG1vZGVscyBpbiB0aGUgbGlzdA0KICBtb2RlbHNbW3Bhc3RlMChpZF9pLCAiX2xtXzAiKV1dIDwtIGJybSgNCiAgICBiZihmb3JtdWxhX2xtXzAsIGZhbWlseSA9IGdhdXNzaWFuKCkpLCANCiAgICBkYXRhID0gZGZfaSwgY29yZXMgPSA0LCBzZWVkID0gc2VlZF9udW1iZXIsIHNhdmVfcGFycyA9IHNhdmVfcGFycyhhbGwgPSBzYXZlX21vZGVscyksDQogICAgc2FtcGxlX3ByaW9yID0gRkFMU0UsIHNpbGVudCA9IFRSVUUsIGZpbGUgPSBwYXN0ZTAobW9kX291dHB1dF93ZCwgIi8iLCBpZF9pLCAiX2xtXzAiKQ0KICApDQogIA0KICBtb2RlbHNbW3Bhc3RlMChpZF9pLCAiX2xtXzEiKV1dIDwtIGJybSgNCiAgICBiZihmb3JtdWxhX2xtXzEsIGZhbWlseSA9IGdhdXNzaWFuKCkpLCANCiAgICBkYXRhID0gZGZfaSwgY29yZXMgPSA0LCBzZWVkID0gc2VlZF9udW1iZXIsIHNhdmVfcGFycyA9IHNhdmVfcGFycyhhbGwgPSBzYXZlX21vZGVscyksDQogICAgc2FtcGxlX3ByaW9yID0gRkFMU0UsIHNpbGVudCA9IFRSVUUsIGZpbGUgPSBwYXN0ZTAobW9kX291dHB1dF93ZCwgIi8iLCBpZF9pLCAiX2xtXzEiKQ0KICApDQogIA0KICBtb2RlbHNbW3Bhc3RlMChpZF9pLCAiX2xtXzIiKV1dIDwtIGJybSgNCiAgICBiZihmb3JtdWxhX2xtXzIsIGZhbWlseSA9IGdhdXNzaWFuKCkpLCANCiAgICBkYXRhID0gZGZfaSwgY29yZXMgPSA0LCBzZWVkID0gc2VlZF9udW1iZXIsIHNhdmVfcGFycyA9IHNhdmVfcGFycyhhbGwgPSBzYXZlX21vZGVscyksDQogICAgc2FtcGxlX3ByaW9yID0gRkFMU0UsIHNpbGVudCA9IFRSVUUsIGZpbGUgPSBwYXN0ZTAobW9kX291dHB1dF93ZCwgIi8iLCBpZF9pLCAiX2xtXzIiKQ0KICApDQogIA0KICBtb2RlbHNbW3Bhc3RlMChpZF9pLCAiX2xtXzMiKV1dIDwtIGJybSgNCiAgICBiZihmb3JtdWxhX2xtXzMsIGZhbWlseSA9IGdhdXNzaWFuKCkpLCANCiAgICBkYXRhID0gZGZfaSwgY29yZXMgPSA0LCBzZWVkID0gMTQzMDE5LCBzYXZlX3BhcnMgPSBzYXZlX3BhcnMoYWxsID0gc2F2ZV9tb2RlbHMpLA0KICAgIHNhbXBsZV9wcmlvciA9IEZBTFNFLCBzaWxlbnQgPSBUUlVFLCBmaWxlID0gcGFzdGUwKG1vZF9vdXRwdXRfd2QsICIvIiwgaWRfaSwgIl9sbV8zIikNCiAgKQ0KICANCiAgIyBSZXR1cm4gdGhlIGxpc3Qgb2YgbW9kZWxzIGZvciB0aGUgY3VycmVudCBJRA0KICByZXR1cm4obW9kZWxzKQ0KfQ0KYGBgDQoNCmBsb2FkX3JkczkoKWA6IEEgY3VzdG9tIGZ1bmN0aW9uIHRvIGxvYWQgYWxsIHJkcyBtb2RlbHMgaW4gYSBkaXJlY3RvcnkgYW5kIHN0b3JlIGluIGEgbGlzdC4gVGFrZXMgYSBkaXJlY3Rvcnkgd2l0aCAnLnJkcycgZmlsZXMNCg0KYGBge3J9DQpsb2FkX3JkcyA8LSBmdW5jdGlvbihtb2RlbF9kdykgew0KICAjIExpc3QgYWxsIC5yZHMgZmlsZXMgaW4gdGhlIGRpcmVjdG9yeQ0KICBtb2RlbF9maWxlX2xpc3QgPC0gbGlzdC5maWxlcyhwYXRoID0gbW9kZWxfZHcsIHBhdHRlcm4gPSAiXFwucmRzJCIsIGZ1bGwubmFtZXMgPSBUUlVFKQ0KICANCiAgIyBJbml0aWFsaXNlIGFuIGVtcHR5IGxpc3QgdG8gc3RvcmUgbW9kZWxzDQogIG1vZGVsX3N0b3JlX2xpc3QgPC0gbGlzdCgpDQogIA0KICAjIEl0ZXJhdGUgdGhyb3VnaCBlYWNoIGZpbGUgYW5kIGxvYWQgdGhlIFJEUw0KICBmb3IgKG1vZF9pIGluIG1vZGVsX2ZpbGVfbGlzdCkgew0KICAgIG1vZCA8LSByZWFkUkRTKGZpbGUgPSBtb2RfaSkgICMgUmVhZCB0aGUgUkRTIGZpbGUNCiAgICBtb2RlbF9uYW1lIDwtIHRvb2xzOjpmaWxlX3BhdGhfc2Fuc19leHQoYmFzZW5hbWUobW9kX2kpKSAgIyBFeHRyYWN0IHRoZSBmaWxlIG5hbWUgd2l0aG91dCBleHRlbnNpb24NCiAgICBtb2RlbF9zdG9yZV9saXN0W1ttb2RlbF9uYW1lXV0gPC0gbW9kICAjIFN0b3JlIGl0IGluIHRoZSBsaXN0DQogIH0NCiAgDQogICMgUmV0dXJuIHRoZSBsaXN0IG9mIG1vZGVscw0KICByZXR1cm4obW9kZWxfc3RvcmVfbGlzdCkNCn0NCmBgYA0KDQoNCmBpbmNyZW1lbnRhbF9yZWdyZXNzaW9uX2JheWVzX2ZpdHMoKWA6IEEgY3VzdG9tIGZ1bmN0aW9uIGZvciBwdWxsaW5nIG1vZGVsIGZpdHMsIGxvbyBhbmQgcjIgdXNpbmcgYGxvbygpYCBhbmQgYGJheWVzX1IyKClgLCByZXNwZWN0aXZlbHkuIFRha2VzIGEgbGlzdCBvZiBtb2RlbHMuICAgDQoNCmBgYHtyfQ0KIyBEZWZpbmUgRnVuY3Rpb24gdG8gUHJvY2VzcyB0aGUgZGF0YSBmb3IgZWFjaCBJRA0KaW5jcmVtZW50YWxfcmVncmVzc2lvbl9iYXllc19maXRzIDwtIGZ1bmN0aW9uKG1vZGVscykgew0KICANCiAgbG9vX3Jlc3VsdHNfbGlzdCA8LSBsaXN0KCkNCiAgDQogICMgSXRlcmF0ZSBvdmVyIHRoZSBuYW1lcyBvZiB0aGUgbW9kZWxzDQogIGZvciAobW9kX25hbWUgaW4gbmFtZXMobW9kZWxzKSkgew0KICAgICMgRXh0cmFjdCB0aGUgbW9kZWwNCiAgICBtb2RfaSA8LSBtb2RlbHNbW21vZF9uYW1lXV0NCiAgICANCiAgICAjIENvbXB1dGUgTE9PIHJlc3VsdHMNCiAgICBtb2RfbG9vX3Jlc3VsdHNfaSA8LSBsb286Omxvbyhtb2RfaSkNCiAgICANCiAgICAjIEV4dHJhY3QgcmVsZXZhbnQgTE9PIG1ldHJpY3MNCiAgICBlbHBkX2xvb19pIDwtIG1vZF9sb29fcmVzdWx0c19pJGVscGRfbG9vDQogICAgcF9sb29faSA8LSBtb2RfbG9vX3Jlc3VsdHNfaSRwX2xvbw0KICAgIGxvb2ljX2kgPC0gbW9kX2xvb19yZXN1bHRzX2kkbG9vaWMNCiAgICANCiAgICAjIENyZWF0ZSBhIGRhdGEgZnJhbWUgd2l0aCBtZXRyaWNzDQogICAgZGZfaSA8LSBkYXRhLmZyYW1lKA0KICAgICAgZWxwZF9sb28gPSBlbHBkX2xvb19pLA0KICAgICAgcF9sb28gPSBwX2xvb19pLA0KICAgICAgbG9vaWMgPSBsb29pY19pLA0KICAgICAgbW9kZWwgPSBtb2RfbmFtZQ0KICAgICkNCiAgICANCiAgICBlc3RfaSA8LSB0aWR5KG1vZF9pLCBlZmZlY3RzID0gImZpeGVkIiwgY29uZi5pbnQgPSBUUlVFKSAlPiUgDQogICAgICBkcGx5cjo6c2VsZWN0KHRlcm0sIGVzdGltYXRlLCBjb25mLmxvdywgY29uZi5oaWdoKSAlPiUgDQogICAgICB0aWR5cjo6cGl2b3Rfd2lkZXIoDQogICAgICAgIG5hbWVzX2Zyb20gPSB0ZXJtLCAgICAgICAgICAgICAgICAjIFVzZSBgdGVybWAgYXMgY29sdW1uIG5hbWVzDQogICAgICAgIHZhbHVlc19mcm9tID0gYyhlc3RpbWF0ZSwgY29uZi5sb3csIGNvbmYuaGlnaCksICAjIFZhbHVlcyB0byBwaXZvdA0KICAgICAgICBuYW1lc19zZXAgPSAiXyIgICAgICAgICAgICAgICAgICAgIyBBZGQgYSBzZXBhcmF0b3IgdG8gY29sdW1uIG5hbWVzDQogICAgICApDQogICAgDQogICAgZGZfaSA8LSBjYmluZChkZl9pLCBlc3RfaSkNCiAgICANCiAgICAjIFN0b3JlIHRoZSBkYXRhIGZyYW1lIGluIHRoZSBsaXN0DQogICAgbG9vX3Jlc3VsdHNfbGlzdFtbbW9kX25hbWVdXSA8LSBkZl9pDQogIH0NCiAgDQogICMgQ29tYmluZCANCiAgbG9vX3Jlc3VsdHNfY29tYmluZWQgPC0gYmluZF9yb3dzKGxvb19yZXN1bHRzX2xpc3QpDQogIA0KICAjIEdldCBSMiANCiAgcjJfcmVzdWx0cyA8LSBtYXBfZGZyKG1vZGVscywgfiBhcy5kYXRhLmZyYW1lKGJheWVzX1IyKC54KSksIC5pZCA9ICJtb2RlbCIpICU+JQ0KICAgIHRpYmJsZTo6cmVtb3ZlX3Jvd25hbWVzKCkNCiAgDQogICMgQ29tYmluZCBSMiBhbmQgbG9vIHJlc3VsdHMgDQogIG1vZGVsX2ZpdF9kZiA8LSBkcGx5cjo6ZnVsbF9qb2luKGxvb19yZXN1bHRzX2NvbWJpbmVkLCByMl9yZXN1bHRzLCBieSA9ICJtb2RlbCIpICU+JSANCiAgICBkcGx5cjo6c2VsZWN0KG1vZGVsLCBldmVyeXRoaW5nKCkpICU+JSANCiAgICBkcGx5cjo6cmVuYW1lKHIyID0gRXN0aW1hdGUsDQogICAgICAgICAgICAgICAgICByMl9lcnJvciA9IEVzdC5FcnJvciwNCiAgICAgICAgICAgICAgICAgIHIyX3EyLjUgPSBRMi41LA0KICAgICAgICAgICAgICAgICAgcjJfcTk3LjUgPSBROTcuNSkgJT4lIA0KICAgIGRwbHlyOjptdXRhdGUoaWQgPSBzdWIoIl8obG1fXFxkKykkIiwgIiIsIG1vZGVsKSwNCiAgICAgICAgICAgICAgICAgIG1vZGVsX3R5cGUgPSBzdWIoIl4uKl8obG1fXFxkKykkIiwgIlxcMSIsIG1vZGVsKSkNCiAgDQogIHJldHVybihtb2RlbF9maXRfZGYpDQp9DQpgYGANCg0KYGJheWVzX21vZF9wcmVkaWN0aW9ucygpYDogVGhpcyBmdW5jdGlvbiBleHRyYWN0cyB0aGUgcHJlZGljdGVkIHZhbHVlcyB1c2luZyBgZml0dGVkKClgIGZyb20gYSBsaXN0IG9mIG1vZGVscyBhbmQgY29tYmluZXMgdGhlbSB3aXRoIHRoZSBvcmlnaW5hbCBkYXRhIGZpbGUgdXNlZCBmb3IgdGhlIG1vZGVsLiBUaGVzZSBhcmUgdGhlICoqcG9zdGVyaW9yIG1lYW4gZml0dGVkIHZhbHVlcyoqIChpLmUuLCB0aGUgZXhwZWN0ZWQgdmFsdWUgb2YgdGhlIHJlc3BvbnNlIHZhcmlhYmxlIGdpdmVuIHRoZSBwcmVkaWN0b3IgdmFyaWFibGVzIGFuZCB0aGUgZXN0aW1hdGVkIHBvc3RlcmlvciBkaXN0cmlidXRpb25zIG9mIHRoZSBwYXJhbWV0ZXJzKSBmb3IgZWFjaCBvYnNlcnZhdGlvbiBpbiB0aGUgZGF0YXNldCwgYWxvbmcgd2l0aCAqKjk1JSBjcmVkaWJsZSBpbnRlcnZhbHMqKi4NCg0KYGBge3J9DQpiYXllc19tb2RfcHJlZGljdGlvbnMgPC0gZnVuY3Rpb24obW9kZWxzLCBvcmlnaW5hbF9kYXRhKSB7DQogIA0KICBwcmVkaWN0aW9uX2xpc3QgPC0gbGlzdCgpDQogIA0KICBmb3IgKG1vZF9uYW1lIGluIG5hbWVzKG1vZGVscykpIHsNCiAgICAjIEV4dHJhY3QgbW9kDQogICAgbW9kX2kgPC0gbW9kZWxzW1ttb2RfbmFtZV1dDQogICAgDQogICAgIyBNYWtlIG1vZGUgcHJlZGljdGlvbnMNCiAgICBtb2RlbF9wcmVkaWN0aW9uc19pIDwtIGFzLmRhdGEuZnJhbWUoZml0dGVkKG1vZF9pLCBzdW1tYXJ5ID0gVFJVRSkpICU+JSANCiAgICAgIGRwbHlyOjptdXRhdGUobW9kZWwgPSBtb2RfbmFtZSwNCiAgICAgICAgICAgICAgICAgICAgaWQgPSBzdWIoIl8obG1fXFxkKykkIiwgIiIsIG1vZF9uYW1lKSwNCiAgICAgICAgICAgICAgICAgICAgbW9kZWxfdHlwZSA9IHN1YigiXi4qXyhsbV9cXGQrKSQiLCAiXFwxIiwgbW9kX25hbWUpKSAlPiUgDQogICAgICBkcGx5cjo6cmVuYW1lKHByZWRfbG93ZXIgPSBRMi41LCBwcmVkX3VwcGVyID0gUTk3LjUsIHByZWRpY3RlZCA9IEVzdGltYXRlLCBwcmVkX2Vycm9yID0gRXN0LkVycm9yKSAlPiUgDQogICAgICBkcGx5cjo6c2VsZWN0KG1vZGVsLCBldmVyeXRoaW5nKCkpDQogICAgDQogICAgaWRfaSA8LSBtb2RlbF9wcmVkaWN0aW9uc19pJGlkWzFdDQogICAgDQogICAgb3JpZ2luYWxfZGF0YV9pIDwtIG9yaWdpbmFsX2RhdGEgJT4lIA0KICAgICAgZHBseXI6OmZpbHRlcihpZCA9PSBpZF9pKQ0KICAgIA0KICAgIG1vZGVsX3ByZWRpY3Rpb25zX29yaWdpbmFsX2kgPC0gY2JpbmQobW9kZWxfcHJlZGljdGlvbnNfaSwgb3JpZ2luYWxfZGF0YV9pKQ0KICAgIA0KICAgIHByZWRpY3Rpb25fbGlzdFtbbW9kX25hbWVdXSA8LSBtb2RlbF9wcmVkaWN0aW9uc19vcmlnaW5hbF9pDQogIH0NCiAgcHJlZGljdGlvbnNfZGYgPC0gYmluZF9yb3dzKHByZWRpY3Rpb25fbGlzdCkgDQogIHJldHVybihwcmVkaWN0aW9uc19kZikNCn0NCmBgYA0KDQoNCmBjYWxjU01SKClgOiAqKmF1dGhvcmVkIGJ5IERlbmlzIENoYWJvdCoqIHVzZWQgdG8gZXN0aW1hdGUgU01SIHdpdGggc2V2ZXJhbCBkaWZmZXJlbnQgbWV0aG9kcyBDbGFpcmVhdXggYW5kIENoYWJvdCAoMjAxNikgXlsxXV4gICANCg0KXlsxXV4gQ2xhaXJlYXV4LCBHLiBhbmQgQ2hhYm90LCBELiAoMjAxNikgUmVzcG9uc2VzIGJ5IGZpc2hlcyB0byBlbnZpcm9ubWVudGFsIGh5cG94aWE6IGludGVncmF0aW9uIHRocm91Z2ggRnJ5J3MgY29uY2VwdCBvZiBhZXJvYmljIG1ldGFib2xpYyBzY29wZS4gSm91cm5hbCBvZiBGaXNoIEJpb2xvZ3kgaHR0cHM6Ly9kb2kub3JnLzEwLjExMTEvamZiLjEyODMzICAgDQoNCmBgYHtyfQ0KY2FsY1NNUiA9IGZ1bmN0aW9uKFksIHE9YygwLjEsMC4xNSwwLjIsMC4yNSwwLjMpLCBHPTE6NCl7DQoJdSA9IHNvcnQoWSkNCgl0aGUuTWNsdXN0IDwtIE1jbHVzdChZLCAgRz1HKQ0KCWNsIDwtIHRoZS5NY2x1c3QkY2xhc3NpZmljYXRpb24NCgkjIHNvbWV0aW1lcywgdGhlIGNsYXNzIGNvbnRhaW5pbmcgU01SIGlzIG5vdCBjYWxsZWQgMQ0KCSMgdGhlIGZvbGxvd2luZyBwcmVzdW1lcyB0aGF0IHdoZW4gY2xhc3MgMSBjb250YWlucyA+IDEwJSBvZiBjYXNlcywgDQoJIyBpdCBjb250YWlucyBTTVIsIG90aGVyd2lzZSB3ZSB0YWtlIGNsYXNzIDINCgljbDIgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShjbCkpDQoJY2wyJGNsIDwtIGFzLm51bWVyaWMobGV2ZWxzKGNsMiRjbCkpDQoJdmFsaWQgPC0gY2wyJEZyZXE+PTAuMSpsZW5ndGgodGltZSkgIA0KCXRoZS5jbCA8LSBtaW4oY2wyJGNsW3ZhbGlkXSkNCglsZWZ0LmRpc3RyIDwtIFlbdGhlLk1jbHVzdCRjbGFzc2lmaWNhdGlvbj09dGhlLmNsXQ0KCW1sbmQgPSB0aGUuTWNsdXN0JHBhcmFtZXRlcnMkbWVhblt0aGUuY2xdDQoJQ1ZtbG5kID0gc2QobGVmdC5kaXN0cikvbWxuZCAqIDEwMA0KCXF1YW50PXF1YW50aWxlKFksIHEpDQoJbG93MTA9bWVhbih1WzE6MTBdKQ0KCWxvdzEwcGMgPSBtZWFuKHVbNjooNSArIHJvdW5kKDAuMSoobGVuZ3RoKHUpLTUpKSldKQ0KCSMgcmVtb3ZlIDUgb3V0bGllcnMsIGtlZXAgbG93ZXN0IDEwJSBvZiB0aGUgcmVzdCwgYXZlcmFnZQ0KCSMgSGVycm1hbm4gJiBFbmRlcnMgMjAwMA0KCXJldHVybihsaXN0KG1sbmQ9bWxuZCwgcXVhbnQ9cXVhbnQsIGxvdzEwPWxvdzEwLCBsb3cxMHBjPWxvdzEwcGMsDQoJCSAgICAgIGNsPWNsLCBDVm1sbmQ9Q1ZtbG5kKSkNCn0NCmBgYA0KDQoNCmBjYWxjTzJjcml0KClgOiAqKmF1dGhvcmVkIGJ5IERlbmlzIENoYWJvdCoqIHVzZWQgdG8gZXN0aW1hdGUgTzJjcml0IChQY3JpcHQpLiBDbGFpcmVhdXggYW5kIENoYWJvdCAoMjAxNikgXlsxXV4gICANCg0KXlsxXV4gQ2xhaXJlYXV4LCBHLiBhbmQgQ2hhYm90LCBELiAoMjAxNikgUmVzcG9uc2VzIGJ5IGZpc2hlcyB0byBlbnZpcm9ubWVudGFsIGh5cG94aWE6IGludGVncmF0aW9uIHRocm91Z2ggRnJ5J3MgY29uY2VwdCBvZiBhZXJvYmljIG1ldGFib2xpYyBzY29wZS4gSm91cm5hbCBvZiBGaXNoIEJpb2xvZ3kgaHR0cHM6Ly9kb2kub3JnLzEwLjExMTEvamZiLjEyODMzICAgDQoNCioqKk5vdGU6IE8yIGlzIGFzc3VtZWQgdG8gYmUgaW4gcGVyY2VudGFnZSBvZiBkaXNzb2x2ZWQgb3h5Z2VuIChETykqKioNCg0KYGBge3J9DQpjYWxjTzJjcml0IDwtIGZ1bmN0aW9uKERhdGEsIFNNUiwgbG93ZXN0TU8yID0gTkEsIGdhcExpbWl0ID0gNCwNCm1heC5uYi5NTzIuZm9yLnJlZyA9IDIwKQ0Kew0KIyBBVVRIT1I6IERlbmlzIENoYWJvdCwgSW5zdGl0dXQgTWF1cmljZS1MYW1vbnRhZ25lLCBERk8sIENhbmFkYQ0KIyBmaXJzdCB2ZXJzaW9uIHdyaXR0ZW4gaW4gSnVuZSAyMDA5DQojIGxhc3QgdXBkYXRlZCBpbiBKYW51YXJ5IDIwMTUNCm1ldGhvZCA9ICJMU19yZWciICMgd2lsbCBiZWNvbWUgInRocm91Z2hfb3JpZ2luIiBpZiBpbnRlcmNlcHQgaXMgPiAwDQppZihpcy5uYShsb3dlc3RNTzIpKSBsb3dlc3RNTzIgPSBxdWFudGlsZShEYXRhJE1PMltEYXRhJERPID49IDgwXSwgcD0wLjA1KQ0KIyBTdGVwIDE6IGlkZW50aWZ5IHBvaW50cyB3aGVyZSBNTzIgaXMgcHJvcG9ydGlvbmFsIHRvIERPDQpnZXFTTVIgPSBEYXRhJE1PMiA+PSBsb3dlc3RNTzINCnBpdm90RE8gPSBtaW4oRGF0YSRET1tnZXFTTVJdKQ0KbGV0aGFsID0gRGF0YSRETyA8IHBpdm90RE8NCk5fdW5kZXJfU01SID0gc3VtKGxldGhhbCkgIyBwb2ludHMgYXZhaWxhYmxlIGZvciByZWdyZXNzaW9uPw0KZmluYWxfTl91bmRlcl9TTVIgPSBsZXRoYWwgIyBzb21lIHBvaW50cyBtYXkgYmUgcmVtb3ZlZCBhdCBTdGVwIDQNCmxhc3RNTzJyZWcgPSBEYXRhJE1PMltEYXRhJERPID09IHBpdm90RE9dICMgbGFzdCBNTzIgd2hlbiByZWd1bGF0aW5nDQppZihOX3VuZGVyX1NNUiA+IDEpIHRoZU1vZCA9IGxtKE1PMn5ETywgZGF0YT1EYXRhW2xldGhhbCxdKQ0KIyBTdGVwIDIsIGFkZCBvbmUgb3IgbW9yZSBwb2ludCBhdCBvciBhYm92ZSBTTVINCiMgMkEsIHdoZW4gdGhlcmUgYXJlIGZld2VyIHRoYW4gMyB2YWxpZCBwb2ludHMgdG8gY2FsY3VsYXRlIGEgcmVncmVzc2lvbg0KaWYoTl91bmRlcl9TTVIgPCAzKXsNCm1pc3NpbmcgPSAzIC0gc3VtKGxldGhhbCkNCm5vdC5sZXRoYWwgPSBEYXRhJERPW2dlcVNNUl0NCkRPbGltaXQgPSBtYXgoc29ydChub3QubGV0aGFsKVsxOm1pc3NpbmddKSAjIGhpZ2hlc3QgRE8gYWNjZXB0YWJsZQ0KIyB0byByZWFjaCBhIE4gb2YgMw0KYWRkZWRQb2ludHMgPSBEYXRhJERPIDw9IERPbGltaXQNCmxldGhhbCA9IGxldGhhbCB8IGFkZGVkUG9pbnRzDQp0aGVNb2QgPSBsbShNTzJ+RE8sIGRhdGE9RGF0YVtsZXRoYWwsXSkNCn0NCiMgMkIsIGFkZCBwaXZvdERPIHRvIHRoZSBmaXQgd2hlbiBTdGVwIDEgeWllbGRlZCAzIG9yIG1vcmUgdmFsdWVzPw0KaWYoTl91bmRlcl9TTVIgPj0gMyl7DQpsZXRoYWxCID0gRGF0YSRETyA8PSBwaXZvdERPICMgaGFzIG9uZSBtb3JlIHZhbHVlIHRoYW4gImxldGhhbCINCnJlZ0EgPSB0aGVNb2QNCnJlZ0IgPSBsbShNTzJ+RE8sIGRhdGE9RGF0YVtsZXRoYWxCLF0pDQpsYXJnZV9zbG9wZV9kcm9wID0gKGNvZWYocmVnQSlbMl0vY29lZihyZWdCKVsyXSkgPiAxLjEgIyBhcmJpdHJhcnkNCmxhcmdlX0RPX2dhcCA9IChtYXgoRGF0YSRET1tsZXRoYWxCXSkgLSBtYXgoRGF0YSRET1tsZXRoYWxdKSkgPiBnYXBMaW1pdA0KdG9vU21hbGxNTzIgPSBsYXN0TU8ycmVnIDwgU01SDQppZighbGFyZ2Vfc2xvcGVfZHJvcCAmICFsYXJnZV9ET19nYXAgJiAhdG9vU21hbGxNTzIpIHsNCmxldGhhbCA9IGxldGhhbEINCnRoZU1vZCA9IHJlZ0INCn0gIyBvdGhlcndpc2Ugd2UgZG8gbm90IGFjY2VwdCB0aGUgYWRkaXRpb25hbCBwb2ludA0KfQ0KIyBTdGVwIDMNCiMgaWYgdGhlIHVzZXIgd2FudHMgdG8gbGltaXQgdGhlIG51bWJlciBvZiBwb2ludHMgaW4gdGhlIHJlZ3Jlc3Npb24NCmlmKCFpcy5uYShtYXgubmIuTU8yLmZvci5yZWcpICYgc3VtKGxldGhhbCk+bWF4Lm5iLk1PMi5mb3IucmVnKXsNClJhbmtzID0gcmFuayhEYXRhJERPKQ0KbGV0aGFsID0gUmFua3MgPD0gbWF4Lm5iLk1PMi5mb3IucmVnDQp0aGVNb2QgPSBsbShNTzJ+RE8sIGRhdGE9RGF0YVtsZXRoYWwsXSkNCmZpbmFsX05fdW5kZXJfU01SID0gbWF4Lm5iLk1PMi5mb3IucmVnDQp9DQojIFN0ZXAgNA0KcHJlZE1PMiA9IGFzLm51bWVyaWMocHJlZGljdCh0aGVNb2QsIGRhdGEuZnJhbWUoRE89RGF0YSRETykpKQ0KRGF0YSRkZWx0YSA9IChEYXRhJE1PMi1wcmVkTU8yKS9wcmVkTU8yICogMTAwICMgcmVzaWR1YWxzIHNldCB0byB6ZXJvDQojIHdoZW4gYmVsb3cgcGl2b3RETw0KRGF0YSRkZWx0YVtEYXRhJERPIDwgcGl2b3RETyB8IGxldGhhbF0gPSAwDQp0b2wgPSAwICMgYW55IHBvc2l0aXZlIHJlc2lkdWFsIGlzIHVuYWNjZXB0YWJsZQ0KSGlnaFZhbHVlcyA9IERhdGEkZGVsdGEgPiB0b2wNClJhbmtzID0gcmFuaygtMSpEYXRhJGRlbHRhKQ0KSGlnaE1PMiA9IEhpZ2hWYWx1ZXMgJiBSYW5rcyA9PSBtaW4oUmFua3MpICMga2VlcCBsYXJnZXN0IHJlc2lkdWFsDQppZiAoc3VtKEhpZ2hWYWx1ZXMpID4gMCkgew0KbmJsZXRoYWwgPSBzdW0obGV0aGFsKQ0KRGF0YSRXID0gTkENCkRhdGEkV1tsZXRoYWxdPTEvbmJsZXRoYWwNCkRhdGEkV1tIaWdoTU8yXSA9IDENCnRoZU1vZCA9IGxtKE1PMn5ETywgd2VpZ2h0PVcsIGRhdGE9RGF0YVtsZXRoYWwgfCBIaWdoTU8yLF0pDQojIFRoaXMgbmV3IHJlZ3Jlc3Npb24gaXMgYWx3YXlzIGFuIGltcHJvdmVtZW50LCBidXQgdGhlcmUgY2FuIHN0aWxsDQojIGJlIHBvaW50cyBhYm92ZSB0aGUgbGluZSwgc28gd2UgcmVwZWF0DQpwcmVkTU8yXzIgPSBhcy5udW1lcmljKHByZWRpY3QodGhlTW9kLCBkYXRhLmZyYW1lKERPPURhdGEkRE8pKSkNCkRhdGEkZGVsdGEyID0gKERhdGEkTU8yLXByZWRNTzJfMikvcHJlZE1PMl8yICogMTAwDQpEYXRhJGRlbHRhMltEYXRhJERPIDwgcGl2b3RET10gPSAwDQp0b2wgPSBEYXRhJGRlbHRhMltIaWdoTU8yXQ0KSGlnaFZhbHVlczIgPSBEYXRhJGRlbHRhMiA+IHRvbA0KaWYoc3VtKEhpZ2hWYWx1ZXMyKT4wKXsNClJhbmtzMiA9IHJhbmsoLTEqRGF0YSRkZWx0YTIpDQpIaWdoTU8yXzIgPSBIaWdoVmFsdWVzMiAmIFJhbmtzMiA9PSAxICMga2VlcCB0aGUgbGFyZ2VzdCByZXNpZHVhbA0KbmJsZXRoYWwgPSBzdW0obGV0aGFsKQ0KRGF0YSRXID0gTkENCkRhdGEkV1tsZXRoYWxdPTEvbmJsZXRoYWwNCkRhdGEkV1tIaWdoTU8yXzJdID0gMQ0KdGhlTW9kMiA9IGxtKE1PMn5ETywgd2VpZ2h0PVcsIGRhdGE9RGF0YVtsZXRoYWwgfCBIaWdoTU8yXzIsXSkNCiMgaXMgbmV3IHNsb3BlIHN0ZWVwZXIgdGhhbiB0aGUgb2xkIG9uZT8NCmlmKHRoZU1vZDIkY29lZlsyXSA+IHRoZU1vZCRjb2VmWzJdKSB7DQp0aGVNb2QgPSB0aGVNb2QyDQpIaWdoTU8yID0gSGlnaE1PMl8yDQp9DQp9ICMgZW5kIHNlY29uZCBzZWFyY2ggZm9yIGhpZ2ggdmFsdWUNCn0gIyBlbmQgZmlyc3Qgc2VhcmNoIGZvciBoaWdoIHZhbHVlDQpDb2VmID0gY29lZmZpY2llbnRzKHRoZU1vZCkNCiNTdGVwIDUsIGNoZWNrIGZvciBwb3NpdGl2ZSBpbnRlcmNlcHQNCkFib3ZlT3JpZ2luID0gQ29lZlsxXSA+IDANCiMgaWYgaXQgaXMsIHdlIHVzZSBhIHJlZ3Jlc3Npb24gdGhhdCBnb2VzIHRocm91Z2ggdGhlIG9yaWdpbg0KaWYgKEFib3ZlT3JpZ2luKXsNCnRoZU1vZCA9IGxtKE1PMn5ETyAtMSwgZGF0YT1EYXRhW2xldGhhbCxdKQ0KQ29lZiA9IGMoMCwgY29lZmZpY2llbnRzKHRoZU1vZCkpICMgbmVlZCB0byBhZGQgdGhlIGludGVyY2VwdCAoMCkNCiMgbWFudWFsbHkgdG8gaGF2ZSBhIHBhaXIgb2YgY29lZmZpY2llbnRzDQptZXRob2QgPSAidGhyb3VnaF9vcmlnaW4iDQpIaWdoTU8yID0gcmVwKEZBTFNFLCBucm93KERhdGEpKSAjIGRpZCBub3QgdXNlIHRoZSBhZGRpdGlvbmFsIHZhbHVlDQojIGZyb20gU3RlcCA0DQp9DQpwbzJjcml0ID0gYXMubnVtZXJpYyhyb3VuZCgoU01SIC0gQ29lZlsxXSkgLyBDb2VmWzJdLCAxKSkNCnN1bV9tb2QgPSBzdW1tYXJ5KHRoZU1vZCkNCmFub3ZfbW9kID0gYW5vdmEodGhlTW9kKQ0KTzJDUklUID0gbGlzdChvMmNyaXQ9cG8yY3JpdCwgU01SPVNNUiwgTmJfTU8yX2NvbmZvcm1pbmcgPSBOX3VuZGVyX1NNUiwNCk5iX01PMl9jb25mX3VzZWQgPSBmaW5hbF9OX3VuZGVyX1NNUiwNCkhpZ2hfTU8yX3JlcXVpcmVkID0gc3VtKEhpZ2hNTzIpID09IDEsIG9yaWdEYXRhPURhdGEsDQpNZXRob2Q9bWV0aG9kLCBtb2Q9dGhlTW9kLCByMiA9IHN1bV9tb2Qkci5zcXVhcmVkLA0KUCA9IGFub3ZfbW9kJCJQcig+RikiLCBsZXRoYWxQb2ludHMgPSB3aGljaChsZXRoYWwpLA0KQWRkZWRQb2ludHMgPSB3aGljaChIaWdoTU8yKSkNCn0gIyBlbmQgZnVuY3Rpb24NCmBgYA0KDQpgcGxvdE8yY3JpdCgpYDogKiphdXRob3JlZCBieSBEZW5pcyBDaGFib3QqKiwgdXNlZCB0byBwbG90IHRoZSBtb2RlcyB1c2VkIGZvciB0aGUgYGNhbGNPMmNyaXQoKWAgZnVuY3Rpb24uIENsYWlyZWF1eCBhbmQgQ2hhYm90ICgyMDE2KSBeWzFdXiAgIA0KDQoqKipOb3RlOiBJIGFkZGVkIGFibGluZShoPWxvd2VzdE1PMiwgY29sPSJwaW5rIikgc28gdGhhdCBJIGNvdWxkIHZpc3VhbGlzZSB0aGUgbG93ZXN0TU8yIHBvc2l0aW9uKioqDQoNCmBgYHtyfQ0KcGxvdE8yY3JpdCA8LSBmdW5jdGlvbihvMmNyaXRvYmosIHBsb3RJRD0iIiwNClhsYWI9IkRpc3NvbHZlZCBveHlnZW4gKCUgc2F0LikiLCBZbGFiPSJkb3RpdGFsdW1vbCIsDQpzbXIuY2V4PTAuOSwgbzJjcml0LmNleD0wLjksIHBsb3RJRC5jZXg9MS4yLA0KVHJhbnNwYXJlbmN5PVQsLi4uKQ0Kew0KIyBBVVRIT1I6IERlbmlzIENoYWJvdCwgSW5zdGl0dXQgTWF1cmljZS1MYW1vbnRhZ25lLCBERk8sIENhbmFkYQ0KIyBmaXJzdCB2ZXJzaW9uIHdyaXR0ZW4gaW4gSnVuZSAyMDA5DQojIGxhc3QgdXBkYXRlZCAyMDE1LTAyLTA5DQojIGZvciBSIHBsb3R0aW5nIGRldmljZXMgdGhhdCBkbyBub3Qgc3VwcG9ydCB0cmFuc3BhcmVuY3kNCiMgKGUuZy4sIHBvc3RzY3JpcHQpLCBzZXQgVHJhbnNwYXJlbmN5IHRvIEZBTFNFDQpzbXIgPSBvMmNyaXRvYmokU01SDQppZihZbGFiICVpbiUgYygiZG90aXRhbHVtb2wiLCAiaXRhbHVtb2wiLCAiZG90dW1vbCIsICJ1bW9sIiwNCiJkb3RpdGFsbWciLCAiaXRhbG1nIiwgImRvdG1nIiwgIm1nIikpIHsNCnN3aXRjaChZbGFiLA0KZG90aXRhbHVtb2wgPSB7DQptbzIubGFiID0gZXhwcmVzc2lvbihwYXN0ZShpdGFsaWMoZG90KE0pKVtPWzJdXSwgIiAoIixtdSwibW9sICIsIE9bMl0sDQoiICIsIG1pbl4tMSwgIiAiLCBrZ14tMSwgIikiKSkNCn0sDQppdGFsdW1vbCA9IHsNCm1vMi5sYWIgPSBleHByZXNzaW9uKHBhc3RlKGl0YWxpYyhNKVtPWzJdXSwgIiAoIixtdSwibW9sICIsIE9bMl0sICIgIiwNCm1pbl4tMSwgIiAiLCBrZ14tMSwgIikiKSkNCn0sDQpkb3R1bW9sID0gew0KbW8yLmxhYiA9IGV4cHJlc3Npb24ocGFzdGUoZG90KE0pW09bMl1dLCAiICgiLG11LCJtb2wgIiwgT1syXSwgIiAiLA0KbWluXi0xLCAiICIsIGtnXi0xLCAiKSIpKQ0KfSwNCnVtb2wgPSB7DQptbzIubGFiID0gZXhwcmVzc2lvbihwYXN0ZShNW09bMl1dLCAiICgiLG11LCJtb2wgIiwgT1syXSwgIiAiLCBtaW5eLTEsDQoiICIsIGtnXi0xLCAiKSIpKQ0KfSwNCmRvdGl0YWxtZyA9IHsNCm1vMi5sYWIgPSBleHByZXNzaW9uKHBhc3RlKGl0YWxpYyhkb3QoTSkpW09bMl1dLCAiIChtZyAiLCBPWzJdLCAiICIsDQpoXi0xLCAiICIsIGtnXi0xLCAiKSIpKQ0KfSwNCml0YWxtZyA9IHsNCm1vMi5sYWIgPSBleHByZXNzaW9uKHBhc3RlKGl0YWxpYyhNKVtPWzJdXSwgIiAobWcgIiwgT1syXSwgIiAiLA0KaF4tMSwgIiAiLCBrZ14tMSwgIikiKSkNCn0sDQpkb3RtZyA9IHsNCm1vMi5sYWIgPSBleHByZXNzaW9uKHBhc3RlKGRvdChNKVtPWzJdXSwgIiAobWcgIiwgT1syXSwgIiAiLCBoXi0xLCAiICIsDQprZ14tMSwgIikiKSkNCn0sDQptZyA9IHsNCm1vMi5sYWIgPSBleHByZXNzaW9uKHBhc3RlKE1bT1syXV0sICIgKG1nICIsIE9bMl0sICIgIiwgaF4tMSwgIiAiLA0Ka2deLTEsICIpIikpDQp9DQopDQp9IGVsc2UgbW8yLmxhYj1ZbGFiDQppZihUcmFuc3BhcmVuY3kpIHtDb2w9YyhyZ2IoMCwwLDAsMC43KSwgInJlZCIsICJvcmFuZ2UiKQ0KfSBlbHNlIHtDb2w9YyhncmV5KDAuMyksICJyZWQiLCAib3JhbmdlIil9DQpEYXRhPW8yY3JpdG9iaiRvcmlnRGF0YQ0KbG93ZXN0TU8yID0gcXVhbnRpbGUoRGF0YSRNTzJbRGF0YSRETyA+PSA4MF0sIHA9MC4wNSkgIyBJIGFkZGVkIHRoaXMNCkRhdGEkQ29sb3IgPSBDb2xbMV0NCkRhdGEkQ29sb3JbbzJjcml0b2JqJGxldGhhbFBvaW50c10gPSBDb2xbMl0NCkRhdGEkQ29sb3JbbzJjcml0b2JqJEFkZGVkUG9pbnRzXSA9IENvbFszXQ0KIyBvcmRpbmFyeSBMUyByZWdyZXNzaW9uIHdpdGhvdXQgYWRkZWQgcG9pbnRzOiBibHVlIGxpbmUsIHJlZCBzeW1ib2xzDQojIG9yZGluYXJ5IExTIHJlZ3Jlc3Npb24gd2l0aCBhZGRlZCBwb2ludHM6IGJsdWUgbGluZSwgcmVkICYgb3JhbmdlIHN5bWJvbHMNCiMgcmVncmVzc2lvbiB0aHJvdWdoIG9yaWdpbjogZ3JlZW4gZG90dGVkIGxpbmUsIHJlZCBzeW1ib2xzDQpsaW5lLmNvbG9yID0gaWZlbHNlKG8yY3JpdG9iaiRNZXRob2Q9PSJMU19yZWciLCAiYmx1ZSIsICJkYXJrZ3JlZW4iKQ0KbGluZS50eXBlID0gaWZlbHNlKG8yY3JpdG9iaiRNZXRob2Q9PSJMU19yZWciLCAxLCAzKQ0KbGltWCA9IGMoMCwgbWF4KERhdGEkRE8pKQ0KbGltWSA9IGMoMCwgbWF4KERhdGEkTU8yKSkNCnBsb3QoTU8yfkRPLCBkYXRhPURhdGEsIHhsaW09bGltWCwgeWxpbT1saW1ZLCBjb2w9RGF0YSRDb2xvciwgeGxhYj1YbGFiLA0KeWxhYj1tbzIubGFiLCAuLi4pDQpjb29yZCA8LSBwYXIoInVzciIpDQppZihwbG90SUQgIT0gIiIpew0KdGV4dCgwLCBjb29yZFs0XSwgcGxvdElELCBjZXg9cGxvdElELmNleCwgYWRqPWMoMCwxLjIpKQ0KfQ0KYWJsaW5lKGg9bG93ZXN0TU8yLCBjb2w9InBpbmsiKSAjIEkgYWRkZWQgdGhpcw0KYWJsaW5lKGg9c21yLCBjb2w9Im9yYW5nZSIpDQp0ZXh0KGNvb3JkWzFdLCBzbXIsICJTTVIiLCBhZGo9YygtMC4xLDEuMyksIGNleD1zbXIuY2V4KQ0KdGV4dChjb29yZFsxXSwgc21yLCByb3VuZChzbXIsMSksIGFkaj1jKC0wLjEsLTAuMyksIGNleD1zbXIuY2V4KQ0KaWYoIWlzLm5hKG8yY3JpdG9iaiRvMmNyaXQpKSB7DQphYmxpbmUobzJjcml0b2JqJG1vZCwgY29sPWxpbmUuY29sb3IsIGx0eT1saW5lLnR5cGUpDQpzZWdtZW50cyhvMmNyaXRvYmokbzJjcml0LCBzbXIsIG8yY3JpdG9iaiRvMmNyaXQsIGNvb3JkWzNdLA0KY29sPWxpbmUuY29sb3IsIGx3ZD0xKQ0KdGV4dCh4PW8yY3JpdG9iaiRvMmNyaXQsIHk9MCwgbzJjcml0b2JqJG8yY3JpdCwgY29sPWxpbmUuY29sb3IsDQpjZXg9bzJjcml0LmNleCwgYWRqPWMoLTAuMSwwLjUpKQ0KfQ0KfSAjIGVuZCBvZiBmdW5jdGlvbg0KYGBgDQoNCg0KPCEtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tPg0KIyBXb3JraW5nIGRpcmVjdG9yaWVzIA0KPCEtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tPg0KDQojIyBJbnB1dA0KDQoqKm1ldGFfZmlsZXNfd2QqKjogRGlyZWN0b3J5IGZvciB0aGUgbWV0YWRhdGENCg0KYGBge3J9DQp3ZCA8LSBnZXR3ZCgpDQptZXRhX2ZpbGVzX3dkIDwtIHBhc3RlMCh3ZCwgIi4vbWV0YS1kYXRhIikgIyBjcmVhdGVzIGEgdmFyaWFibGUgd2l0aCB0aGUgbmFtZSBvZiB0aGUgd2Qgd2Ugd2FudCB0byB1c2UNCmBgYA0KDQoqKmxhYmNoYXJ0X3dkKio6IERpcmVjdG9yeSBmb3IgTGFiY2hhcnQgZXN0aW1hdGVkIHNsb3Blcw0KDQpgYGB7cn0NCmxhYmNoYXJ0X3dkIDwtIHBhc3RlMCh3ZCwgIi4vbGFiLWNoYXJ0LXNsb3BlcyIpDQpgYGANCg0KKiptb2RfZGF0YV93ZCoqOiBEaXJlY3RvcnkgZm9yIG1vZGVsIG91dHB1dCBkYXRhIGVzdGltYXRlZCBzbG9wZXMNCg0KYGBge3J9DQptb2RfZGF0YV93ZCA8LSBwYXN0ZTAod2QsICIuL21vZC1kYXRhIikNCmBgYA0KDQoNCiMjIE91dHB1dA0KDQoqKm91dHB1dF9maWdfd2QqKjogdGhpcyBpcyB3aGVyZSB3ZSB3aWxsIHB1dCB0aGUgZmlndXJlcw0KDQpgYGB7ciwgZWNobyA9IEZBTFNFfQ0Kb3V0cHV0X2ZpZ193ZCA8LSBwYXN0ZTAod2QsICIuL291dHB1dC1maWciKQ0KaWZlbHNlKCFkaXIuZXhpc3RzKCJvdXRwdXQtZmlnIiksIGRpci5jcmVhdGUoIm91dHB1dC1maWciKSwgIkZvbGRlciBhbHJlYWR5IGV4aXN0cyIpDQpgYGANCg0KKipvdXRwdXRfbW9kc193ZCoqOiB0aGlzIGlzIHdoZXJlIHdlIHdpbGwgcHV0IHRoZSBtb2RlbHMNCg0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCm91dHB1dF9tb2RzX3dkIDwtIHBhc3RlMCh3ZCwgIi4vb3V0cHV0LW1vZCIpDQppZmVsc2UoIWRpci5leGlzdHMoIm91dHB1dC1tb2QiKSwgZGlyLmNyZWF0ZSgib3V0cHV0LW1vZCIpLCAiRm9sZGVyIGFscmVhZHkgZXhpc3RzIikNCmBgYA0KDQo8IS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0+DQojIElucHV0IGZpbGVzDQo8IS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0+DQoNCiMjIFNsb3BlcyAoTU88c3ViPjI8L3N1Yj4pDQoNCioqc2xvcGVfZGYqKjogV2UgaGF2ZSBpbXBvcnRlZCB0aGUgc2xvcGVzIGV4dHJhY3RlZCBpbiBMYWJDaGFydCBkdXJpbmcgZWFjaCBwaGFzZSBvZiB0aGUgZXhwZXJpbWVudA0KDQpgYGB7cn0NCiBzZXR3ZChsYWJjaGFydF93ZCkNCiMgDQojICMgR2V0IHRoZSBuYW1lcyBvZiBhbGwgc2hlZXRzIGluIHRoZSBFeGNlbCBmaWxlDQpzaGVldF9uYW1lcyA8LSBleGNlbF9zaGVldHMoImxhYmNoYXJ0LWFsbC1kYXRlc192Mi54bHN4IikNCmFsbF90cmlhbHNfc2VsZWN0IDwtIGMoInN0YXJ0X2RhdGUiLCAib3JkZXIiLCAicGhhc2UiLCAiY3ljbGUiLCAiZGF0ZSIsICJ0aW1lIikNCnNsb3BlX2xpc3QgPC0gbGlzdCgpDQoNCmZvciAoc2hlZXQgaW4gc2hlZXRfbmFtZXMpIHsNCg0KICBkZiA8LSByZWFkX2V4Y2VsKCJsYWJjaGFydC1hbGwtZGF0ZXNfdjIueGxzeCIsIHNoZWV0ID0gc2hlZXQpICU+JSANCiAgZHBseXI6OnJlbmFtZV93aXRoKHRvbG93ZXIpDQogIA0KYV9uYW1lIDwtIHBhc3RlMCgiYV8iLCB0b2xvd2VyKHNoZWV0KSkNCmFfZGYgPC0gZGYgJT4lDQogIGRwbHlyOjpzZWxlY3Qoc3RhcnRzX3dpdGgoJ2EnKSwgYWxsX3RyaWFsc19zZWxlY3QpICU+JSANCiAgZHBseXI6OnJlbmFtZSh0ZW1wID0gYV90ZW1wKSAlPiUgDQogIGRwbHlyOjptdXRhdGUoYWNyb3NzKHN0YXJ0c193aXRoKCdhJyksIGFzLm51bWVyaWMpKSAlPiUgDQogIHBpdm90X2xvbmdlcigNCiAgICBjb2xzID0gc3RhcnRzX3dpdGgoJ2EnKSwgIyBTZWxlY3QgYWxsIGNvbHVtbnMgdG8gcGl2b3QNCiAgICBuYW1lc190byA9IGMoImNoYW1iZXJfaWQiLCAiLnZhbHVlIiksICMgU2VwYXJhdGUgY29sdW1uIG5hbWVzIGludG8gJ2lkJyBhbmQgb3RoZXIgdmFyaWFibGVzDQogICAgbmFtZXNfc2VwID0gIl8iDQogICkgJT4lDQogIGRwbHlyOjptdXRhdGUocmVzcGlyb21ldGVyX2dyb3VwID0gImEiKSAjIEFkZCBhIG5ldyBjb2x1bW4gd2l0aCBhIGZpeGVkIHZhbHVlDQoNCnNsb3BlX2xpc3RbW2FfbmFtZV1dPC0gYV9kZg0KDQpiX25hbWUgPC0gcGFzdGUwKCJiXyIsIHRvbG93ZXIoc2hlZXQpKQ0KYl9kZiA8LSBkZiAlPiUgDQogIGRwbHlyOjpzZWxlY3Qoc3RhcnRzX3dpdGgoJ2InKSwgYWxsX3RyaWFsc19zZWxlY3QpICU+JSANCiAgZHBseXI6OnJlbmFtZSh0ZW1wID0gYl90ZW1wKSAlPiUgDQogIGRwbHlyOjptdXRhdGUoYWNyb3NzKHN0YXJ0c193aXRoKCdiJyksIGFzLm51bWVyaWMpKSAlPiUgDQogIHBpdm90X2xvbmdlcigNCiAgICBjb2xzID0gc3RhcnRzX3dpdGgoJ2InKSwgIyBTZWxlY3QgYWxsIGNvbHVtbnMgdG8gcGl2b3QNCiAgICBuYW1lc190byA9IGMoImNoYW1iZXJfaWQiLCAiLnZhbHVlIiksICMgU2VwYXJhdGUgY29sdW1uIG5hbWVzIGludG8gJ2lkJyBhbmQgb3RoZXIgdmFyaWFibGVzDQogICAgbmFtZXNfc2VwID0gIl8iDQogICkgJT4lIA0KICAgIGRwbHlyOjptdXRhdGUocmVzcGlyb21ldGVyX2dyb3VwID0gImIiKQ0KDQpzbG9wZV9saXN0W1tiX25hbWVdXSA8LSBiX2RmDQoNCmNfbmFtZSA8LSBwYXN0ZTAoImNfIiwgdG9sb3dlcihzaGVldCkpDQpjX2RmIDwtIGRmICU+JSANCiAgZHBseXI6OnNlbGVjdChzdGFydHNfd2l0aCgnYycpLCBhbGxfdHJpYWxzX3NlbGVjdCkgJT4lIA0KICBkcGx5cjo6cmVuYW1lKHRlbXAgPSBjX3RlbXAsDQogICAgICAgICAgICAgICAgaV9jeWNsZSA9IGN5Y2xlKSAlPiUgDQogIGRwbHlyOjptdXRhdGUoYWNyb3NzKHN0YXJ0c193aXRoKCdjJyksIGFzLm51bWVyaWMpKSAlPiUNCiAgcGl2b3RfbG9uZ2VyKA0KICAgIGNvbHMgPSBzdGFydHNfd2l0aCgnYycpLCAjIFNlbGVjdCBhbGwgY29sdW1ucyB0byBwaXZvdA0KICAgIG5hbWVzX3RvID0gYygiY2hhbWJlcl9pZCIsICIudmFsdWUiKSwgIyBTZXBhcmF0ZSBjb2x1bW4gbmFtZXMgaW50byAnaWQnIGFuZCBvdGhlciB2YXJpYWJsZXMNCiAgICBuYW1lc19zZXAgPSAiXyINCiAgKSAlPiUgDQogICAgZHBseXI6Om11dGF0ZShyZXNwaXJvbWV0ZXJfZ3JvdXAgPSAiYyIpICU+JSANCiAgZHBseXI6OnJlbmFtZShjeWNsZSA9IGlfY3ljbGUpDQoNCnNsb3BlX2xpc3RbW2NfbmFtZV1dIDwtIGNfZGYNCg0KZF9uYW1lIDwtIHBhc3RlMCgiZF8iLCB0b2xvd2VyKHNoZWV0KSkNCmRfZGYgPC0gZGYgJT4lIA0KICBkcGx5cjo6c2VsZWN0KHN0YXJ0c193aXRoKCdkJyksIGFsbF90cmlhbHNfc2VsZWN0KSAlPiUgDQogIGRwbHlyOjpyZW5hbWUodGVtcCA9IGRfdGVtcCwNCiAgICAgICAgICAgICAgICBpX2RhdGUgPSBkYXRlKSAlPiUgDQogIGRwbHlyOjptdXRhdGUoYWNyb3NzKHN0YXJ0c193aXRoKCdkJyksIGFzLm51bWVyaWMpKSAlPiUNCiAgcGl2b3RfbG9uZ2VyKA0KICAgIGNvbHMgPSBzdGFydHNfd2l0aCgnZCcpLCAjIFNlbGVjdCBhbGwgY29sdW1ucyB0byBwaXZvdA0KICAgIG5hbWVzX3RvID0gYygiY2hhbWJlcl9pZCIsICIudmFsdWUiKSwgIyBTZXBhcmF0ZSBjb2x1bW4gbmFtZXMgaW50byAnaWQnIGFuZCBvdGhlciB2YXJpYWJsZXMNCiAgICBuYW1lc19zZXAgPSAiXyINCiAgKSAlPiUgDQogICAgZHBseXI6Om11dGF0ZShyZXNwaXJvbWV0ZXJfZ3JvdXAgPSAiZCIpICU+JSANCiAgZHBseXI6OnJlbmFtZShkYXRlID0gaV9kYXRlKQ0KDQpzbG9wZV9saXN0W1tkX25hbWVdXSA8LSBkX2RmDQp9DQoNCg0Kc2xvcGVfZGYgPC0gYmluZF9yb3dzKHNsb3BlX2xpc3QpICU+JSANCiAgZHBseXI6Om11dGF0ZShyZXNwX2NhdF9kYXRlID0gcGFzdGUwKHJlc3Bpcm9tZXRlcl9ncm91cCwgIl8iLCBzdGFydF9kYXRlKSwNCiAgICAgICAgICAgICAgICBjaGFtYmVyX24gPSBzdHJfZXh0cmFjdChjaGFtYmVyX2lkLCAiXFxkKyIpLA0KICAgICAgICAgICAgICAgIGlkX3Byb3ggPSBwYXN0ZTAocmVzcF9jYXRfZGF0ZSwgIl8iLCBjaGFtYmVyX24pLA0KICAgICAgICAgICAgICAgIHRpbWVfaG1zID0gYXNfaG1zKHRpbWUqMzYwMCksDQogICAgICAgICAgICAgICAgZGF0ZV9jaHIgPSBmb3JtYXQoZGF0ZSwgIiVkLyVtLyVZIikNCiAgICAgICAgICAgICAgICApDQpgYGANCg0KIyMgTWV0YWRhdGENCg0KKiptZXRhZGF0YSoqOiBUaGlzIGlzIHRoZSBtZXRhIGRhdGEgZm9yIGVhY2ggY2hhbWJlciAgIA0KDQoqTm90ZTogV2UgYXJlIGFsc28gYWRkaW5nIHZvbHVtZSBiYXNlZCBvbiBjaGFtYmVyIHR5cGUuKiAgIA0KDQpgYGB7cn0NCnNldHdkKG1ldGFfZmlsZXNfd2QpDQoNCm1ldGFkYXRhIDwtIHJlYWRfZXhjZWwoIk1vcnBoby54bHN4IiwgbmEgPSAiTkEiKSAlPiUgDQogIGRwbHlyOjptdXRhdGUoaWRfc3BsaXQgPSBpZCkgJT4lIA0KICB0aWR5cjo6c2VwYXJhdGUoaWRfc3BsaXQsIGludG8gPSBjKCJyZXNwaXJvbWV0ZXJfZ3JvdXAiLCAic2FsaW5pdHlfZ3JvdXAiLCAic3RhcnRfZGF0ZSIsICJjaGFtYmVyIiksIHNlcCA9ICJfIikgJT4lIA0KICBkcGx5cjo6bXV0YXRlKA0KICAgICAgdm9sdW1lID0gZHBseXI6OmNhc2Vfd2hlbigNCiAgICAgICAgY2hhbWJlcl90eXBlID09ICJMIiB+IDAuMzAwLA0KICAgICAgICBjaGFtYmVyX3R5cGUgPT0gIk1fTSIgfiAwLjEwNSwNCiAgICAgICAgY2hhbWJlcl90eXBlID09ICJNX05NIiB+IDAuMTEsDQogICAgICAgIGNoYW1iZXJfdHlwZSA9PSAiUyIgfiAwLjA1OCwNCiAgICAgICAgY2hhbWJlcl90eXBlID09ICJTTSIgfiAwLjA3NSwNCiAgICAgICAgY2hhbWJlcl90eXBlID09ICJEMyIgfiAwLjA1NSwNCiAgICAgICAgVFJVRSB+IE5BDQogICAgICApLA0KICAgICAgaWRfcHJveCA9IHBhc3RlMChyZXNwaXJvbWV0ZXJfZ3JvdXAsICJfIiwgc3RhcnRfZGF0ZSwgIl8iLCBjaGFtYmVyKSwNCiAgICAgIHJlbF9zaXplID0gbWFzcy92b2x1bWUpDQpgYGANCg0KDQojIyMgVXJiaW5hLCBHbG92ZXIsIGFuZCBGb3JzdGVyICgyMDEyKQ0KDQoqKnVyYmluYV9ldF9hbF8yMDEyKio6IFRoaXMgaXMgdGhlIG1lYW4gbGV2ZWwgZGF0YSBleHRyYWN0ZWQgZnJvbSBVcmJpbmEsIEdsb3ZlciwgYW5kIEZvcnN0ZXIgKDIwMTIpXlsyXV4gRmlndXJlIDFhLiBXZSB1c2VkIHRoZSBtZXRhRGlnaXRpc2UgcGFja2FnZSBpbiBSIHRvIGV4dHJhY3QgdGhlIGRhdGEgXlszXV4uICAgDQoNCmBgYHtyfQ0Kc2V0d2QobWV0YV9maWxlc193ZCkNCg0KdXJiaW5hX2V0X2FsXzIwMTIgPC0gcmVhZC5jc3YoInVyYmluYS1ldC1hbC0yMDEyLWZpZzFhLmNzdiIpDQpgYGANCg0KIyMjIENvbWJpbmRpbmcgbWV0YWRhdGENCg0KQWRkaW5nIHRoZSBtZXRhIGRhdGEgdG8gTGFiQ2hhcnQgc2xvcGVzICAgDQoNCmBgYHtyfQ0Kc2xvcGVfZGZfMiA8LSBzbG9wZV9kZiAlPiUgDQogIGRwbHlyOjpzZWxlY3QoLXN0YXJ0X2RhdGUsIC1yZXNwaXJvbWV0ZXJfZ3JvdXApICU+JSANCiAgbGVmdF9qb2luKG1ldGFkYXRhLCBieSA9ICJpZF9wcm94IikgJT4lIA0KICBkcGx5cjo6bXV0YXRlKGxpZ2h0X2RhcmsgPSBpZl9lbHNlKHRpbWVfaG1zID49IGFzLmhtcygiMDc6MDA6MDAiKSAmIHRpbWVfaG1zIDwgYXMuaG1zKCIxOTowMDowMCIpLCAibGlnaHQiLCAiZGFyayIpKSAlPiUgDQogIGRwbHlyOjphcnJhbmdlKGlkKQ0KYGBgDQoNCjwhLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLT4NCiMgRGF0YQ0KPCEtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tPg0KDQojIyBOdW1iZXJzIA0KDQpXZSBoYXZlICoqNjQgZmlzaCoqIHdpdGggTU8yIGRhdGEgICANCg0KYGBge3J9DQpuIDwtIHNsb3BlX2RmXzIgJT4lIA0KICBkcGx5cjo6ZmlsdGVyKGNoYW1iZXJfY29uZGl0aW9uID09ICJmaXNoIikgJT4lIA0KICBkcGx5cjo6ZGlzdGluY3QoaWQpICU+JSANCiAgbnJvdyguKQ0KDQpwYXN0ZTAoIm4gPSAiLCBuKQ0KYGBgDQoNCldpdGggNDggZnJvbSB0aGUgMCBwcHQgYW5kIDQ4IGZyb20gOSBwcHQgZ3JvdXBzICAgDQoNCmBgYHtyfQ0Kc2xvcGVfZGZfMiAlPiUgDQogIGRwbHlyOjpmaWx0ZXIoY2hhbWJlcl9jb25kaXRpb24gPT0gImZpc2giKSAlPiUNCiAgZHBseXI6Omdyb3VwX2J5KHNhbGluaXR5X2dyb3VwKSAlPiUgDQogIGRwbHlyOjpyZWZyYW1lKCduIHRvdGFsJyA9IGxlbmd0aCh1bmlxdWUoaWQpKSkgJT4lIA0KICBndCgpICU+JSANCiAgY29sc19sYWJlbCgNCiAgICBzYWxpbml0eV9ncm91cCA9ICJTYWxpbml0eSBncm91cCINCiAgKSAlPiUgDQogIGNvbHNfYWxpZ24oDQogICAgYWxpZ24gPSAiY2VudGVyIiwgDQogICAgY29sdW1ucyA9IGV2ZXJ5dGhpbmcoKQ0KICApDQpgYGANCg0KIyMgRmlzaCBzaXplDQoNCkhlcmUgd2UgY2FsY3VsYXRlIHRoZSBtZWFuIGxlbmd0aCBhbmQgc2l6ZSBvZiBmaXNoIHVzZWQgaW4gdGhlIGV4cGVyaW1lbnQuICAgDQoNCmBgYHtyfQ0KbWFzc19sZW5ndGggPC0gc2xvcGVfZGZfMiAlPiUgDQogIGRwbHlyOjpmaWx0ZXIoY2hhbWJlcl9jb25kaXRpb24gPT0gImZpc2giKSAlPiUNCiAgZHBseXI6Omdyb3VwX2J5KGlkKSAlPiUgDQogIGRwbHlyOjpzYW1wbGVfbigxKSAlPiUgDQogIGRwbHlyOjp1bmdyb3VwKCkgJT4lIA0KICBkcGx5cjo6cmVmcmFtZSh4X21hc3MgPSByb3VuZChtZWFuKG1hc3MsIG5hLnJtID0gVFJVRSksIDMpLA0KICAgICAgICAgICAgICAgICBtaW5fbWFzcyA9IHJvdW5kKG1pbihtYXNzLCBuYS5ybSA9IFRSVUUpLCAzKSwNCiAgICAgICAgICAgICAgICAgbWF4X21hc3MgPSByb3VuZChtYXgobWFzcywgbmEucm0gPSBUUlVFKSwgMyksDQogICAgICAgICAgICAgICAgIHhfbGVuZ3RoID0gcm91bmQobWVhbihsZW5ndGgsIG5hLnJtID0gVFJVRSksIDIpLA0KICAgICAgICAgICAgICAgICBtaW5fbGVuZ3RoID0gcm91bmQobWluKGxlbmd0aCwgbmEucm0gPSBUUlVFKSwgMiksDQogICAgICAgICAgICAgICAgIG1heF9sZW5ndGggPSByb3VuZChtYXgobGVuZ3RoLCBuYS5ybSA9IFRSVUUpLCAyKSkNCg0KbWFzc19tZWFuIDwtIG1hc3NfbGVuZ3RoICU+JSANCiAgcHVsbCh4X21hc3MpDQoNCm1hc3NfbWluIDwtIG1hc3NfbGVuZ3RoICU+JSANCiAgcHVsbChtaW5fbWFzcykNCg0KbWFzc19tYXggPC0gbWFzc19sZW5ndGggJT4lIA0KICBwdWxsKG1heF9tYXNzKQ0KDQpsZW5ndGhfbWVhbiA8LSBtYXNzX2xlbmd0aCAlPiUgDQogIHB1bGwoeF9sZW5ndGgpDQoNCmxlbmd0aF9taW4gPC0gbWFzc19sZW5ndGggJT4lIA0KICBwdWxsKG1pbl9sZW5ndGgpDQoNCmxlbmd0aF9tYXggPC0gbWFzc19sZW5ndGggJT4lIA0KICBwdWxsKG1heF9sZW5ndGgpDQoNCnBhc3RlMCgiVGhlIG1lYW4gbWFzcyBvZiBmaXNoIHdhcyAiLCBtYXNzX21lYW4sICIgZyAocmFuZ2U6ICIsIG1hc3NfbWluLCAiLSIsIG1hc3NfbWF4LCAiKSIsDQogICAgICAgIiwgYW5kIHRoZSBtZWFuIGxlbmd0aCB3YXMgIiwgbGVuZ3RoX21lYW4sICIgbW0gKHJhbmdlOiAiLCBsZW5ndGhfbWluLCAiLSIsIGxlbmd0aF9tYXgsICIpIikNCmBgYA0KDQpIZXJlIHdlIGNhbGN1bGF0ZSB0aGUgbWVhbiBsZW5ndGggYW5kIHNpemUgb2YgZmlzaCB1c2VkIGluIHRoZSBleHBlcmltZW50ICAgDQoNCmBgYHtyfQ0KbWFzc19yZWxfbGVuZ3RoIDwtIHNsb3BlX2RmXzIgJT4lIA0KICBkcGx5cjo6ZmlsdGVyKGNoYW1iZXJfY29uZGl0aW9uID09ICJmaXNoIikgJT4lDQogIGRwbHlyOjpncm91cF9ieShpZCkgJT4lIA0KICBkcGx5cjo6c2FtcGxlX24oMSkgJT4lIA0KICBkcGx5cjo6dW5ncm91cCgpICMlPiUgDQojICAgZHBseXI6OnJlZnJhbWUobWVhbl9yZWxfc2l6ZSA9IHJvdW5kKG1lYW4ocmVsX3NpemUsIG5hLnJtID0gVFJVRSksIDMpLCANCiMgICAgICAgICAgICAgICAgICBtaW5fcmVsX3NpemUgPSByb3VuZChtaW4ocmVsX3NpemUsIG5hLnJtID0gVFJVRSksIDMpLA0KIyAgICAgICAgICAgICAgICAgIG1heF9yZWxfc2l6ZSA9IHJvdW5kKG1heChyZWxfc2l6ZSwgbmEucm0gPSBUUlVFKSwgMykpDQojIA0KIyByZWxfc2l6ZV9tZWFuIDwtIG1hc3NfcmVsX2xlbmd0aCAlPiUgDQojICAgcHVsbChtZWFuX3JlbF9zaXplKQ0KIyANCiMgcmVsX3NpemVfbWluIDwtIG1hc3NfcmVsX2xlbmd0aCAlPiUgDQojICAgcHVsbChtaW5fcmVsX3NpemUpDQojIA0KIyByZWxfc2l6ZV9tYXggPC0gbWFzc19yZWxfbGVuZ3RoICU+JSANCiMgICBwdWxsKG1heF9yZWxfc2l6ZSkNCiMgDQojIHBhc3RlMCgiVGhlIG1lYW4gcmVsYXRpdmUgc2l6ZSBvZiBmaXNoIHRvIHRoaWVyIHJlc3Bpcm9tZXRyeSBjaGFtYmVyIChtYXNzIGcgcGVyIHZvbHVtZSBMKSB3YXMgIiwgcmVsX3NpemVfbWVhbiwgIiAocmFuZ2U6ICIsIHJlbF9zaXplX21pbiwgIi0iLCByZWxfc2l6ZV9tYXgsICIpIikNCmBgYA0KDQojIyBGaWx0ZXJpbmcgdHJpYWxzDQoNCldlIHdpbGwgcmVtb3ZlIDYgdHJpYWxzIHdoaWNoIGhhZCBlcnJvcnMuIFRoZXNlIGFyZSBhcyBmb2xsb3dzOiAgIA0KDQotCWFfMF8yNW5vdl8zIGZpc2ggZGllZCBkdXJpbmcgdHJpYWwNCi0JYl8wXzI2bm92XzQgZmxhdCBsaW5lZCBlYXJseQ0KLQljXzBfMjJub3ZfMiBjaGFtYmVyIHdhcyBvcGVuZWQgZWFybHkNCi0JY185XzI2bm92XzIgY2hhbWJlciB3YXMgb3BlbmVkIGVhcmx5DQotCWNfOV8yNm5vdl80IGNoYW1iZXIgd2FzIG9wZW5lZCBlYXJseQ0KLQlkXzlfMjdub3ZfMyBzZW5zb3Igd2FzIGp1bXB5IGFuZCBlbmQgcG9pbnRzIHdlcmUgaGFyZCB0byBjb25maWRlbnRseSBJRCB2aXN1YWxseSAgIA0KDQoNCmBgYHtyfQ0KcmVtb3ZlX3RyaWFsX2Vycm9yIDwtIGMoImFfMF8yNW5vdl8zIiwgImJfMF8yNm5vdl80IiwgImNfMF8yMm5vdl8yIiwgImNfOV8yNm5vdl8yIiwgImNfOV8yNm5vdl80IiwgImRfOV8yN25vdl8zIikNCg0Kc2xvcGVfZGZfZmlsdGVyIDwtIHNsb3BlX2RmXzIgJT4lIA0KICBkcGx5cjo6ZmlsdGVyKCEoaWQgJWluJSByZW1vdmVfdHJpYWxfZXJyb3IpKQ0KYGBgDQoNCldlIG5vdyBoYXZlICoqNTggZmlzaCoqIHdpdGggTU8yIGRhdGEgICANCg0KYGBge3J9DQpuIDwtIHNsb3BlX2RmX2ZpbHRlciAlPiUgDQogIGRwbHlyOjpmaWx0ZXIoY2hhbWJlcl9jb25kaXRpb24gPT0gImZpc2giKSAlPiUgDQogIGRwbHlyOjpkaXN0aW5jdChpZCkgJT4lIA0KICBucm93KC4pDQoNCnBhc3RlMCgibiA9ICIsIG4pDQpgYGANCg0KV2l0aCAzMCBpbiB0aGUgMCBwcHQgZ3JvdXAgYW5kIDI4IGluIHRoZSA5IHBwdCBncm91cCAgIA0KDQpgYGB7cn0NCnNsb3BlX2RmX2ZpbHRlciAlPiUgDQogIGRwbHlyOjpmaWx0ZXIoY2hhbWJlcl9jb25kaXRpb24gPT0gImZpc2giKSAlPiUNCiAgZHBseXI6Omdyb3VwX2J5KHNhbGluaXR5X2dyb3VwKSAlPiUgDQogIGRwbHlyOjpyZWZyYW1lKCduIHRvdGFsJyA9IGxlbmd0aCh1bmlxdWUoaWQpKSkgJT4lIA0KICBndCgpICU+JSANCiAgY29sc19sYWJlbCgNCiAgICBzYWxpbml0eV9ncm91cCA9ICJTYWxpbml0eSBncm91cCINCiAgKSAlPiUgDQogIGNvbHNfYWxpZ24oDQogICAgYWxpZ24gPSAiY2VudGVyIiwgDQogICAgY29sdW1ucyA9IGV2ZXJ5dGhpbmcoKQ0KICApDQpgYGANCg0KIyMgRmlsdGVyaW5nIE1PPHN1Yj4yPC9zdWI+IGVzdGltYXRlcw0KDQpIZXJlIHdlIGFwcGx5IHRoZSBmb2xsb3dpbmcgZmlsdGVycyB0byB0aGUgTU88c3ViPjI8L3N1Yj4gZGF0YTogIA0KDQotIFJlbW92ZSB0aGUgZmlyc3QgNSBTTVIgY3ljbGVzIChidXJuIGluKQ0KLSBSZW1vdmUgYWxsIHBvc2l0aXZlIHJhdyBzbG9wZXMNCi0gUmVtb3ZlIGFsbCBNTzIgY2FsY3VsYXRlZCB1c2luZyBsZXNzIHRoZW4gNjAgZGF0YSBwb2ludHMgKDUgbWluKSANCi0gUmVtb3ZlIGFsbCBNTzIgY2FsY3VsYXRlZCBpZiBPPHN1Yj4yPC9zdWI+IGluY3JlYXNlcyBpbiBhIGNsb3NlZCBwaGFzZSAoaS5lLiB0cmlhbCBoYXMgZW5kZWQpICANCg0KDQpDaGVjayBwb3NpdGl2ZSB2YWx1ZXMgZm9yIE1PMiBiZWZvcmUgcmVtb3ZpbmcuICANCg0KYGBge3J9DQpzbG9wZV90aWR5X3JlbW92ZV9mbHVzaCA8LSBzbG9wZV9kZl9maWx0ZXIgJT4lDQogIGRwbHlyOjpmaWx0ZXIocGhhc2UgIT0gInNtciIsIGNoYW1iZXJfY29uZGl0aW9uID09ICJmaXNoIikgJT4lIA0KICBkcGx5cjo6Z3JvdXBfYnkoaWQpICU+JQ0KICBkcGx5cjo6YXJyYW5nZShpZCwgb3JkZXIpICU+JSAgIyBFbnN1cmUgdGhlIGRhdGEgaXMgc29ydGVkIHdpdGhpbiBlYWNoIGdyb3VwDQogIGRwbHlyOjptdXRhdGUobzJfZGlmZiA9IGlmX2Vsc2Uocm93X251bWJlcigpID09IDEsIDAsIG8yIC0gbGFnKG8yKSksICMgQ2FsY3VsYXRlIHRoZSBkaWZmZXJlbmNlIGluICdvMicNCiAgICAgICAgICAgICAgICBvMl9kaWZmX2N1bXN1bSA9IGN1bXN1bShvMl9kaWZmID4gMSkpICU+JSAgIyBDaGVja3MgZmlyc3Qgb2NjdXJyZW5jZSBhbmQgc3VtcyANCiAgZHBseXI6OmZpbHRlcihvMl9kaWZmX2N1bXN1bSA9PSAwKSAlPiUgICMgS2VlcCByb3dzIHVudGlsIHRoZSBmaXJzdCBqdW1wID4gMQ0KICBkcGx5cjo6dW5ncm91cCgpICU+JQ0KICBkcGx5cjo6c2VsZWN0KC1vMl9kaWZmLCAtbzJfZGlmZl9jdW1zdW0pDQoNCnBvc3RpdmVfc2xvcGVzIDwtIHNsb3BlX3RpZHlfcmVtb3ZlX2ZsdXNoICU+JQ0KICBkcGx5cjo6ZmlsdGVyKGNoYW1iZXJfY29uZGl0aW9uID09ICJmaXNoIiAmIG1vMmNvcnIgPiAwKQ0KDQpsaXN0X3Bvc3RpdmVfYWxsIDwtIHNsb3BlX2RmX2ZpbHRlciAlPiUgDQogIGRwbHlyOjpmaWx0ZXIoY2hhbWJlcl9jb25kaXRpb24gPT0gImZpc2giICYgbW8yY29yciA+IDAgJiBwaGFzZSA9PSAic21yIikgJT4lIA0KICBkcGx5cjo6YmluZF9yb3dzKC4sIHBvc3RpdmVfc2xvcGVzKSAlPiUgDQogIGRwbHlyOjpkaXN0aW5jdChpZCkgJT4lIA0KICBkcGx5cjo6cHVsbChpZCkNCg0KcHJpbnQocGFzdGUwKCJUaGVyZSBhcmUgIiwgbGVuZ3RoKGxpc3RfcG9zdGl2ZV9hbGwpLCAiIGZpc2ggd2l0aCBwb3N0aXZlIHNsb3Blcy4gVGhlc2UgZmlzaCBhcmU6ICIsIHBhc3RlKGxpc3RfcG9zdGl2ZV9hbGwsIGNvbGxhcHNlID0gIiwgIiksICIuIikpDQpgYGANCg0KRmlsdGVyaW5nIHRoZSBNTzIgZGF0YSAgIA0KDQpgYGB7cn0NCmN5Y2xlX2J1cm4gPC0gMDo0DQoNCnNsb3BlX2RmX2ZpbHRlcl8xIDwtIHNsb3BlX2RmX2ZpbHRlciAlPiUNCiAgZHBseXI6OmZpbHRlcighKGN5Y2xlICVpbiUgY3ljbGVfYnVybikgJiANCiAgICAgICAgICAgICAgICAgIG1vMmNvcnIgPCAwICYgDQogICAgICAgICAgICAgICAgICBuID4gNjAgJg0KICAgICAgICAgICAgICAgICAgY2hhbWJlcl9jb25kaXRpb24gPT0gImZpc2giDQogICAgICAgICAgICAgICAgKQ0KICANCiMgRmlsdGVyIG91dCB0aGUgZW5kIGZsdXNoDQpzbG9wZV90aWR5X2Nsb3NlZCA8LSBzbG9wZV9kZl9maWx0ZXJfMSAlPiUNCiAgZHBseXI6OmZpbHRlcihwaGFzZSAhPSAic21yIikgJT4lIA0KICBkcGx5cjo6Z3JvdXBfYnkoaWQpICU+JQ0KICBkcGx5cjo6YXJyYW5nZShpZCwgb3JkZXIpICU+JSAgIyBFbnN1cmUgdGhlIGRhdGEgaXMgc29ydGVkIHdpdGhpbiBlYWNoIGdyb3VwDQogIGRwbHlyOjptdXRhdGUobzJfZGlmZiA9IGlmX2Vsc2Uocm93X251bWJlcigpID09IDEsIDAsIG8yIC0gbGFnKG8yKSksICMgQ2FsY3VsYXRlIHRoZSBkaWZmZXJlbmNlIGluICdvMicNCiAgICAgICAgICAgICAgICBvMl9kaWZmX2N1bXN1bSA9IGN1bXN1bShvMl9kaWZmID4gMSkpICU+JSAgIyBDaGVja3MgZmlyc3Qgb2NjdXJyZW5jZSBhbmQgc3VtcyANCiAgZHBseXI6OmZpbHRlcihvMl9kaWZmX2N1bXN1bSA9PSAwKSAlPiUgICMgS2VlcCByb3dzIHVudGlsIHRoZSBmaXJzdCBqdW1wID4gMQ0KICBkcGx5cjo6dW5ncm91cCgpICU+JQ0KICBkcGx5cjo6c2VsZWN0KC1vMl9kaWZmLCAtbzJfZGlmZl9jdW1zdW0pDQoNCnNsb3BlX3RpZHlfc21yIDwtIHNsb3BlX2RmX2ZpbHRlcl8xICU+JSANCiAgZHBseXI6OmZpbHRlcihwaGFzZSA9PSAic21yIikNCg0Kc2xvcGVfZGZfZmlsdGVyXzIgPC0gcmJpbmQoc2xvcGVfdGlkeV9zbXIsIHNsb3BlX3RpZHlfY2xvc2VkKSAlPiUgDQogIGRwbHlyOjphcnJhbmdlKGlkLCBvcmRlcikNCmBgYA0KDQojIyBDYWxjdWxhdGluZyBTTVINCg0KV2Ugd2lsbCBjYWxjdWxhdGUgU01SIHVzaW5nIGNhbGNTTVIgZnVuY3Rpb24gYnkgQ2hhYm90LCBTdGVmZmVuc2VuIGFuZCBGYXJyZWxsICgyMDE2KV5bMV1eLiBTcGVjaWZpY2FsbHksIHdlIHVzZSBtZWFuIG9mIHRoZSBsb3dlc3Qgbm9ybWFsIGRpc3RyaWJ1dGlvbiAoTUxORCkgd2hlcmUgQ1ZtbG5kIDwgNS40IGFuZCB0aGUgbWVhbiBvZiB0aGUgbG93ZXIgMjAlIHF1YW50aWxlIChxMC4yKSB3ZXJlIENWbWxuZCA+IDUuNC4gSWYgQ1ZtbG5kIGlzIG5vdCBjYWxjdWxhdGVkIHdlIGhhdmUgdXNlZCBxMC4yLiAgDQoNCmBgYHtyfQ0KbGFiY2hhcnRfY2hhYm90X3NtciA8LSBzbG9wZV9kZl9maWx0ZXJfMiAlPiUNCiAgZHBseXI6OmZpbHRlcihwaGFzZSA9PSAic21yIikNCg0KIyBFeHRyYWN0IGRpc3RpbmN0IElEcw0KaWRzIDwtIGxhYmNoYXJ0X2NoYWJvdF9zbXIgJT4lIA0KICBkcGx5cjo6ZGlzdGluY3QoaWQpICU+JSANCiAgZHBseXI6OnB1bGwoKQ0KDQojIEluaXRpYWxpc2UgYW4gZW1wdHkgbGlzdCB0byBzdG9yZSBTTVIgZGF0YQ0Kc21yX2xpc3QgPC0gbGlzdCgpDQoNCiMgUHJvY2VzcyBlYWNoIElEDQpmb3IgKGlkX2kgaW4gaWRzKSB7DQogIHRyeUNhdGNoKHsNCiAgICAjIEZpbHRlciB0aGUgZGF0YSBmb3IgdGhlIGN1cnJlbnQgSUQNCiAgICBkZl9pIDwtIGxhYmNoYXJ0X2NoYWJvdF9zbXIgJT4lIA0KICAgICAgZHBseXI6OmZpbHRlcihpZCA9PSBpZF9pKSAlPiUgDQogICAgICBkcGx5cjo6bXV0YXRlKGFic19tbzJjb3JyID0gYWJzKG1vMmNvcnIpKQ0KICAgIA0KICAgICMgQ2FsY3VsYXRlIFNNUiByZXN1bHRzDQogICAgY2FsY1NNUl9yZXN1bHRzIDwtIGNhbGNTTVIoZGZfaSRhYnNfbW8yY29ycikNCiAgICBDVm1sbmRfaSA8LSBjYWxjU01SX3Jlc3VsdHMkQ1ZtbG5kDQogICAgcXVhbnRfaSA8LSBjYWxjU01SX3Jlc3VsdHMkcXVhbnQgJT4lIGFzX3RpYmJsZSgpDQogICAgcXVhbnRfMjBwZXJfaSA8LSBxdWFudF9pJHZhbHVlWzNdDQogICAgbWxuZF9pIDwtIGNhbGNTTVJfcmVzdWx0cyRtbG5kDQogICAgc21yX3ZhbHVlIDwtIGlmX2Vsc2UoQ1ZtbG5kX2kgPCA1LjQsIG1sbmRfaSwgcXVhbnRfMjBwZXJfaSkNCiAgICBzbXJfdHlwZSA8LSBpZl9lbHNlKENWbWxuZF9pIDwgNS40LCAibWxuZCIsICJxdWFudF8yMHBlciIpDQogICAgc21yX3ZhbHVlIDwtIGlmX2Vsc2UoaXMubmEoc21yX3ZhbHVlKSwgcXVhbnRfMjBwZXJfaSwgc21yX3ZhbHVlKQ0KICAgIHNtcl90eXBlIDwtIGlmX2Vsc2UoaXMubmEoc21yX3R5cGUpLCAicXVhbnRfMjBwZXIiLCBzbXJfdHlwZSkNCiAgICANCiAgICAjIENyZWF0ZSBhIGRhdGEgZnJhbWUgZm9yIHRoZSBjdXJyZW50IElEDQogICAgc21yX2RmIDwtIHRpYmJsZSgNCiAgICAgIGlkID0gaWRfaSwNCiAgICAgIHNtciA9IHNtcl92YWx1ZSwNCiAgICAgIHNtcl9lc3QgPSBzbXJfdHlwZQ0KICAgICkNCiAgICANCiAgfSwgZXJyb3IgPSBmdW5jdGlvbihlKSB7DQogICAgIyBIYW5kbGUgZXJyb3JzIGJ5IGFzc2lnbmluZyBOQSB2YWx1ZXMNCiAgICBzbXJfZGYgPC0gdGliYmxlKA0KICAgICAgaWQgPSBpZF9pLA0KICAgICAgc21yID0gTkEsDQogICAgICBzbXJfZXN0ID0gTkENCiAgICApDQogIH0pDQogIA0KICAjIEFwcGVuZCB0byB0aGUgbGlzdA0KICBzbXJfbGlzdFtbaWRfaV1dIDwtIHNtcl9kZg0KfQ0KDQojIENvbWJpbmUgYWxsIGluZGl2aWR1YWwgU01SIGRhdGEgZnJhbWVzIGludG8gb25lDQpzbXJfZGYgPC0gYmluZF9yb3dzKHNtcl9saXN0KSAlPiUgDQogIGRwbHlyOjpyZW5hbWUoc21yX2NoYWJvdCA9IHNtciwNCiAgICAgICAgICAgICAgICBzbXJfY2hhYm90X21ldGhvZCA9IHNtcl9lc3QpDQoNCnNsb3BlX2RmX2ZpbHRlcl8zIDwtIHNsb3BlX2RmX2ZpbHRlcl8yICU+JQ0KICBkcGx5cjo6bGVmdF9qb2luKC4sIHNtcl9kZiwgYnkgPSAiaWQiKQ0KYGBgDQoNCiMjIFRyYW5zZm9ybWluZyBNTzxzdWI+Mjwvc3ViPg0KDQpIZXJlIHdlIGFyZSB0cmFuc2Zvcm1pbmcgdGhlIE1PPHN1Yj4yPC9zdWI+IHVuaXRzLiBUaGUgcmVzdWx0aW5nIHZhbHVlcyBhcmUgYXMgZm9sbG93czogIA0KDQotICoqTU8yKiogaXMgYWJzb2x1dGUgdmFsdWUgb2YgdGhlIGJhY2tncm91bmQgYW5kIGxlYWsgY29ycmVjdGVkIE1PPHN1Yj4yPC9zdWI+IHNsb3BlIGZyb20gTGFiY2hhcnQgKG1vMmNvcnIpIHRpbWVzIHRoZSBuZXQgdm9sdW1lIG9mIHRoZSBjaGFtYmVyICh2b2x1bWUgLSBmaXNoIG1hc3MpLCDDlyA2MCwgw5cgNjAsIHRvIGFjaGlldmUgTU88c3ViPjI8L3N1Yj4gYXMgbWdeLTFeIE88c3ViPjI8L3N1Yj4gaF4tMV4NCg0KLSAqKk1PMl9nKiogaXMgTU8yIGRpdmlkZWQgYnkgZmlzaCBtYXNzIHRvIGFjaGlldmUgTU88c3ViPjI8L3N1Yj4gYXMgbWdeLTFeIE88c3ViPjI8L3N1Yj4gZ14tMV4gaF4tMV4gKGkuZS4gbWFzcyBzdGFuZGFyZGlzZWQpDQoNCi0gKipTTVIqKiBhYnNvbHV0ZSB2YWx1ZSBvZiB0aGUgU01SIGVzdGltYXRlcyB1c2luZyBtZXRob2RzIGRlc2NyaWJlZCBieSBDaGFib3QsIFN0ZWZmZW5zZW4gYW5kIEZhcnJlbGwgKDIwMTYpXlsxXV4gdGltZXMgdGhlIG5ldCB2b2x1bWUgb2YgdGhlIGNoYW1iZXIgKHZvbHVtZSAtIGZpc2ggbWFzcyksIMOXIDYwLCDDlyA2MCwgdG8gYWNoaWV2ZSBTTVIgYXMgbWdeLTFeIE88c3ViPjI8L3N1Yj4gZ14tMV4gaF4tMV4pDQoNCi0gKipTTVJfZyoqIGlzIFNNUiBkaXZpZGVkIGJ5IGZpc2ggbWFzcyB0byBhY2hpZXZlIFNNUiBhcyBtZ14tMV4gTzxzdWI+Mjwvc3ViPiBnXi0xXiBoXi0xXiAoaS5lLiBtYXNzIHN0YW5kYXJkaXNlZCkNCg0KLSAqKkRPKiogaXMgZGlzc29sdmVkIG94eWdlbiBwZXJjZW50YWdlIGNhbGN1bGF0ZWQgZnJvbSBPPHN1Yj4yPC9zdWI+IHZhbHVlcyAobWdeLTFeIExeLTFeKSB1c2luZyB0aGUgcmVjb3JkZWQgdGVtcGVyYXR1cmUsIHNhbGluaXR5LCBhbmQgYSBjb25zdGFudCBhdG1vc3BoZXJpYyBwcmVzc3VyZSAoUGE7IDEwMTMuMjUpDQoNCi0gKipvMl9rcGEqKiBpcyB0aGUgTzxzdWI+Mjwvc3ViPiBjb25jZW50cmF0aW9uIGluIGtpbG9wYXNjYWwgKGtwYSkuIFRoaXMgaXMgdXNlZCB0byBtYWtlIGEgY29tcGFyYXRpdmUgZmlndXJlIG9ubHkuICAgDQoNCg0KYGBge3J9DQojIENvbWJpbmUgYmFjayBpbnRvIG9uZSBkYXRhIGZyYW1lDQpzbG9wZV90aWR5IDwtIHNsb3BlX2RmX2ZpbHRlcl8zICU+JSANCiAgICBkcGx5cjo6bXV0YXRlKERPID0gY29udl9vMigNCiAgICAgICAgICAgICAgICAgICBvMiA9IG8yLA0KICAgICAgICAgICAgICAgICAgIGZyb20gPSAibWdfcGVyX2wiLA0KICAgICAgICAgICAgICAgICAgIHRvID0gInBlcmNlbnRfYS5zLiIsDQogICAgICAgICAgICAgICAgICAgdGVtcCA9IHRlbXAsICNDDQogICAgICAgICAgICAgICAgICAgc2FsID0gbWVhc3VyZWRfc2FsaW5pdHksDQogICAgICAgICAgICAgICAgICAgYXRtX3ByZXMgPSAxMDEzLjI1KSwNCiAgICAgICAgICAgICAgICAgIG8yX2twYSA9IGNvbnZfbzIoDQogICAgICAgICAgICAgICAgICAgbzIgPSBvMiwNCiAgICAgICAgICAgICAgICAgICBmcm9tID0gIm1nX3Blcl9sIiwNCiAgICAgICAgICAgICAgICAgICB0byA9ICJrUGEiLA0KICAgICAgICAgICAgICAgICAgIHRlbXAgPSB0ZW1wLCAjQw0KICAgICAgICAgICAgICAgICAgIHNhbCA9IG1lYXN1cmVkX3NhbGluaXR5LA0KICAgICAgICAgICAgICAgICAgIGF0bV9wcmVzID0gMTAxMy4yNSksDQogICAgICAgICAgICAgICAgICBuZXRfdm9sdW1lID0gdm9sdW1lIC0gbWFzcy8xMDAwLA0KICAgICAgICAgICAgICAgICAgTU8yID0gYWJzKG1vMmNvcnIpKm5ldF92b2x1bWUqNjAqNjAsDQogICAgICAgICAgICAgICAgICBNTzJfZyA9IE1PMi9tYXNzLA0KICAgICAgICAgICAgICAgICAgU01SID0gYWJzKHNtcl9jaGFib3QpKm5ldF92b2x1bWUqNjAqNjAsDQogICAgICAgICAgICAgICAgICBTTVJfZyA9IFNNUi9tYXNzDQogICAgICAgICAgICAgICAgICApDQpgYGANCg0KDQo8IS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0+DQojIFZpc3VhbGlzaW5nIGRhdGENCjwhLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLT4NCg0KIyMgTzxzdWI+Mjwvc3ViPiB2cyBNTzxzdWI+Mjwvc3ViPg0KDQojIyMgRmlndXJlIFNpOiBNZXRhYm9saWMgcmF0ZSBieSBkaXNzb2x2ZWQgb3h5Z2VuIHBlcmNlbnRhZ2UNCg0KVGhpcyB3YXMgdXNlZCB0byBpZGVudGlmeSBhbnkgb3V0bGllcnMsIG9yIHBvdGVudGlhbCBlcnJvcnMuICAgDQoNCmBgYHtyfQ0KbG1fbGluZXMgPC0gc2xvcGVfdGlkeSAlPiUNCiAgZ3JvdXBfYnkoaWQpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgRE9fc2VxID0gbGlzdChzZXEobWluKERPKSwgbWF4KERPKSwgbGVuZ3RoLm91dCA9IDEwMCkpLCAgIyBHZW5lcmF0ZSBhIHNlcXVlbmNlIG9mIERPIHZhbHVlcw0KICAgIE1PMl9wcmVkID0gbGlzdChwcmVkaWN0KGxtKE1PMl9nIH4gRE8sIGRhdGEgPSBjdXJfZGF0YSgpKSwgbmV3ZGF0YSA9IGRhdGEuZnJhbWUoRE8gPSBzZXEobWluKERPKSwgbWF4KERPKSwgbGVuZ3RoLm91dCA9IDEwMCkpKSkNCiAgKSAlPiUNCiAgdW5uZXN0KGMoRE9fc2VxLCBNTzJfcHJlZCkpICAjIEV4cGFuZCBsaXN0cyBpbnRvIHJvd3MNCg0KIyBDcmVhdGUgc2NhdHRlciBwbG90IHdpdGggbWFya2VycyBmb3IgZWFjaCBmaXNoDQpwIDwtIHBsb3RfbHkoDQogIGRhdGEgPSBzbG9wZV90aWR5LA0KICB4ID0gfkRPLA0KICB5ID0gfk1PMl9nLA0KICB0eXBlID0gInNjYXR0ZXIiLA0KICBtb2RlID0gIm1hcmtlcnMiLA0KICBjb2xvciA9IH5pZCwgICMgQ29sb3VyIHBvaW50cyBieSBmaXNoIElEDQogIG1hcmtlciA9IGxpc3Qob3BhY2l0eSA9IDAuNiksDQogIG5hbWUgPSB+aWQNCikNCg0KIyBBZGQgcmVncmVzc2lvbiBsaW5lcyBmb3IgZWFjaCBmaXNoDQpwIDwtIHAgJT4lDQogIGFkZF90cmFjZSgNCiAgICBkYXRhID0gbG1fbGluZXMsDQogICAgeCA9IH5ET19zZXEsDQogICAgeSA9IH5NTzJfcHJlZCwNCiAgICB0eXBlID0gInNjYXR0ZXIiLA0KICAgIG1vZGUgPSAibGluZXMiLA0KICAgIGNvbG9yID0gfmlkLCAgIyBFbnN1cmUgZWFjaCBsaW5lIG1hdGNoZXMgaXRzIGNvcnJlc3BvbmRpbmcgZmlzaA0KICAgIGxpbmUgPSBsaXN0KHdpZHRoID0gMSwgZGFzaCA9ICJzb2xpZCIpLA0KICAgIHNob3dsZWdlbmQgPSBGQUxTRSAgIyBBdm9pZCBjbHV0dGVyaW5nIHRoZSBsZWdlbmQNCiAgKQ0KDQojIEZpbmFsIGxheW91dA0KcCA8LSBwICU+JQ0KICBsYXlvdXQoDQogICAgdGl0bGUgPSAiTU88c3ViPjI8L3N1Yj4gdnMgRGlzc29sdmVkIE94eWdlbiB3aXRoIGluZGl2aWR1YWwgbGluZWFyIHJlZ3Jlc3Npb25zIiwNCiAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiRGlzc29sdmVkIE94eWdlbiAoJSkiKSwNCiAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiTU88c3ViPjI8L3N1Yj4gKG1nPHN1cD4tMTwvc3VwPiBPPHN1Yj4yPC9zdWI+IGc8c3VwPi0xPC9zdXA+IGg8c3VwPi0xPC9zdXA+KSIpLA0KICAgIHNob3dsZWdlbmQgPSBGQUxTRQ0KICApDQoNCiMgRGlzcGxheSBwbG90DQpwDQpgYGANCg0KKioqRmlndXJlIFNpOioqKiBpbnRlcmFjdGl2ZSBwbG90IG9mIG1ldGFib2xpYyByYXRlIG1lYXN1cmVtZW50cyAoTU88c3ViPjI8L3N1Yj47IG1nIE88c3ViPjI8L3N1Yj4gZ14tMV5oXi0xXikgYnkgZGlzc29sdmVkIG94eWdlbiBwZXJjZW50YWdlIChETykgZm9yIGFsbCBmaXNoLCBpbmNsdWRpbmcgYWxsIGVzdGltYXRlcyBkdXJpbmcgdGhlIFNNUiBwaGFzZSAoaS5lLiBpbnRlcm1pdHRlbnQgcGhhc2UpDQoNCg0KIyMjIEZpZ3VyZSBTaTogTWV0YWJvbGljIHJhdGUgYnkgZGlzc29sdmVkIG94eWdlbiBwZXJjZW50YWdlIGZvciBzYWxpbml0eQ0KDQpMb29raW5nIGF0IHRoZSBkaWZmZXJlbmNlIHJlc3BvbnNlcyBpbiB0aGUgdHdvIHNhbGluaXR5IGdyb3Vwcy4gICANCg0KYGBge3J9DQpzbG9wZV90aWR5ICU+JSANCiAgZ2dwbG90KGFlcyh5ID0gTU8yX2csIHggPSBETywgY29sb3VyID0gaWQpKSArICMgRGVmYXVsdCBhZXN0aGV0aWNzDQogIGdlb21fcG9pbnQoc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICBnZW9tX3Ntb290aChhZXMoZ3JvdXAgPSBpZCksIG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UsIGNvbG91ciA9IHNjYWxlczo6YWxwaGEoImJsYWNrIiwgMC41KSkgKyAjIFRyYW5zcGFyZW50IGJsYWNrIGxpbmVzDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gVFJVRSwgY29sb3VyID0gInJlZCIpICsgIyBPdmVyYWxsIHNtb290aCBsaW5lDQogIGdlb21fc21vb3RoKHNlID0gVFJVRSwgY29sb3VyID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgdGhlbWVfY2xlYW4oKSArDQogIGZhY2V0X3dyYXAofnNhbGluaXR5X2dyb3VwKSArDQogIGxhYnMoDQogICAgc3VidGl0bGUgPSAibW8yIHZzIG8yIGJ5IHNhbGluaXR5IHRyZWF0bWVudCIsDQogICAgeCA9ICJEaXNzb2x2ZWQgb3h5Z2VuIHBlcmNlbnRhZ2UgKERPKSIsDQogICAgeSA9ICJNTzIgKE8yIG1nL2cvaCkiDQogICkNCmBgYA0KDQoqKipGaWd1cmUgU2k6KioqDQoNCiMjIyBGaWd1cmUgU2k6IE1ldGFib2xpYyByYXRlIGJ5IGRpc3NvbHZlZCBveHlnZW4gcGVyY2VudGFnZSBmb3IgY2hhbWJlciB0eXBlDQoNCkxvb2tpbmcgYXQgdGhlIGRpZmZlcmVudCBjaGFtYmVyIHR5cGVzICAgDQoNCmBgYHtyfQ0Kc2xvcGVfdGlkeSAlPiUgDQogIGdncGxvdChhZXMoeSA9IE1PMl9nLCB4ID0gRE8sIGNvbG91ciA9IGlkKSkgKyAjIERlZmF1bHQgYWVzdGhldGljcw0KICBnZW9tX3BvaW50KHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgZ2VvbV9zbW9vdGgoYWVzKGdyb3VwID0gaWQpLCBtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFLCBjb2xvdXIgPSBzY2FsZXM6OmFscGhhKCJibGFjayIsIDAuNSkpICsgIyBUcmFuc3BhcmVudCBibGFjayBsaW5lcw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IFRSVUUsIGNvbG91ciA9ICJyZWQiKSArICMgT3ZlcmFsbCBzbW9vdGggbGluZQ0KICBnZW9tX3Ntb290aChzZSA9IFRSVUUsIGNvbG91ciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogIHRoZW1lX2NsZWFuKCkgKw0KICBmYWNldF93cmFwKH5jaGFtYmVyX3R5cGUsIHNjYWxlID0gImZyZWUiKSArDQogIGxhYnMoDQogICAgc3VidGl0bGUgPSAibW8yIHZzIG8yIGJ5IGNoYW1iZXIgdHlwZSIsDQogICAgeCA9ICJEaXNzb2x2ZWQgb3h5Z2VuIHBlcmNlbnRhZ2UgKERPKSIsDQogICAgeSA9ICJNTzIgKG1nIE8yIGcvaCkiDQogICkNCmBgYA0KDQoqKipGaWd1cmUgU2k6KioqICANCiAgDQpIZXJlLCB3ZSBoYXZlIHJlY3JlYXRlZCBhIHNpbWlsYXIgcGxvdCB0byB0aGF0IHByZXNlbnRlZCBpbiBVcmJpbmEsIEdsb3ZlciwgYW5kIEZvcnN0ZXIgKDIwMTIpXlsxXV4gYW5kIGhhdmUgZXh0cmFjdGVkIHRoZSBtZWFuIGxldmVsIGRhdGEgZnJvbSBGaWd1cmUgMWEgdXNpbmcgdGhlIG1ldGFEaWdpdGlzZSBwYWNrYWdlIGluIFJeWzNdXi4gVGhpcyBkYXRhIGlzIGNhbGxlZCB1cmJpbmFfZXRfYWxfMjAxMi4gVGhpcyBhbGxvd3MgdXMgdG8gY29tcGFyZSB0aGUgZGlmZmVyZW5jZXMgaW4gTW8yIGFuZCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gTW8yIGFuZCBPPHN1Yj4yPC9zdWI+LiAgICANCg0KRmlyc3QgbWFraW5nIGEgYmlubmVkIGRhdGEgZnJhbWUgICANCg0KYGBge3J9DQptaW5fbzJfa3BhIDwtIG1pbihzbG9wZV90aWR5JG8yX2twYSwgbmEucm0gPSBUUlVFKQ0KbWF4X28yX2twYSA8LSBtYXgoc2xvcGVfdGlkeSRvMl9rcGEsIG5hLnJtID0gVFJVRSkNCg0KdGltZV9iaW5fZGYgPC0gc2xvcGVfdGlkeSAlPiUNCiAgbXV0YXRlKG8yX2dyb3VwID0gY3V0KG8yX2twYSwgDQogICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEobWluX28yX2twYSwgbWF4X28yX2twYSwgbGVuZ3RoLm91dCA9IDEzKSwgIyAxMSBpbnRlcnZhbHMsIHNvIDEyIGJyZWFrcG9pbnRzDQogICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBwYXN0ZTAoIkdyb3VwICIsIDE6MTIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGUubG93ZXN0ID0gVFJVRSkpICU+JSANCiAgZHBseXI6Omdyb3VwX2J5KG8yX2dyb3VwKSAlPiUgDQogIGRwbHlyOjpyZWZyYW1lKG1lYW5fTU8yX2cgPSBtZWFuKE1PMl9nKSozMS4yNSwNCiAgICAgICAgICAgICAgICAgbWVhbl9vMl9rcGEgPSBtZWFuKG8yX2twYSksDQogICAgICAgICAgICAgICAgIG4gPSBsZW5ndGgoTU8yX2cpKjMxLjI1LA0KICAgICAgICAgICAgICAgICBNTzJfZ19zZCA9IHNkKE1PMl9nKSozMS4yNSwNCiAgICAgICAgICAgICAgICAgbzJfa3BhX3NkID0gc2QobzJfa3BhKSkNCmBgYA0KICANCioqKkZpZ3VyZSBTaToqKiogIA0KICANCiMjIyBGaWd1cmUgU2k6IE1ldGFib2xpYyByYXRlIGJ5IG94eWdlbiBjb25jZW50cmF0aW9uIGZvciBPPHN1Yj4yPC9zdWI+IGJpbnMgd2l0aCBVcmJpbmEgKDIwMTIpDQoNCk5vdyB0aGUgcGxvdCB3aXRoIG91ciBtZWFuIGRhdGEgYW5kIHRoZSBtZWFuIGRhdGEgZnJvbSBVcmJpbmEsIEdsb3ZlciwgYW5kIEZvcnN0ZXIgKDIwMTIpXlsxXV4gICANCg0KYGBge3J9DQpuIDwtIHNsb3BlX3RpZHkgJT4lIA0KICBkcGx5cjo6ZGlzdGluY3QoaWQpICU+JSANCiAgbnJvdyguKQ0KDQojIEV4aXN0aW5nIHBsb3QNCnAgPC0gdGltZV9iaW5fZGYgJT4lIA0KICBnZ3Bsb3QoKSArDQogIGdlb21fcG9pbnQoZGF0YSA9IHNsb3BlX3RpZHksIA0KICAgICAgICAgICAgIGFlcyh5ID0gTU8yX2cgKiAzMS4yNSwgeCA9IG8yX2twYSksIA0KICAgICAgICAgICAgIHNpemUgPSAyLCBjb2xvciA9ICJncmV5IiwgYWxwaGEgPSAwLjMpICsgIA0KICBnZW9tX3BvaW50KGRhdGEgPSB0aW1lX2Jpbl9kZiwgDQogICAgICAgICAgICAgYWVzKHkgPSBtZWFuX01PMl9nLCB4ID0gbWVhbl9vMl9rcGEpLA0KICAgICAgICAgICAgIHNpemUgPSAzLCBjb2xvdXIgPSAiIzBFNEM5MiIsIHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgZ2VvbV9lcnJvcmJhcihkYXRhID0gdGltZV9iaW5fZGYsDQogICAgICAgICAgICAgICAgYWVzKHkgPSBtZWFuX01PMl9nLCB4ID0gbWVhbl9vMl9rcGEsDQogICAgICAgICAgICAgICAgICAgIHltaW4gPSBtZWFuX01PMl9nIC0gTU8yX2dfc2QsIHltYXggPSBtZWFuX01PMl9nICsgTU8yX2dfc2QpLCANCiAgICAgICAgICAgICAgICB3aWR0aCA9IDAuMTUsIGNvbG91ciA9ICIjMEU0QzkyIikgKw0KICBnZW9tX2Vycm9yYmFyaChkYXRhID0gdGltZV9iaW5fZGYsIA0KICAgICAgICAgICAgICAgICBhZXMoeSA9IG1lYW5fTU8yX2csIHggPSBtZWFuX28yX2twYSwNCiAgICAgICAgICAgICAgICAgICAgIHhtaW4gPSBtZWFuX28yX2twYSAtIG8yX2twYV9zZCwgeG1heCA9IG1lYW5fbzJfa3BhICsgbzJfa3BhX3NkKSwgDQogICAgICAgICAgICAgICAgIGhlaWdodCA9IDAuNCwgY29sb3VyID0gIiMwRTRDOTIiKSArDQogIGdlb21fcG9pbnQoZGF0YSA9IHVyYmluYV9ldF9hbF8yMDEyLCANCiAgICAgICAgICAgICBhZXMoeCA9IG8yX21lYW4sIHkgPSBtbzJfbWVhbiksIA0KICAgICAgICAgICAgIHNpemUgPSAzLCBzaGFwZSA9IDIxLCBmaWxsID0gIiNEMjFGM0MiLCBjb2xvciA9ICIjRDIxRjNDIiwgc3Ryb2tlID0gMSkgKw0KICBnZW9tX2Vycm9yYmFyKGRhdGEgPSB1cmJpbmFfZXRfYWxfMjAxMiwgDQogICAgICAgICAgICAgICAgYWVzKHggPSBvMl9tZWFuLCB5bWluID0gbW8yX21lYW4gLSBtbzJfc2QsIHltYXggPSBtbzJfbWVhbiArIG1vMl9zZCksIA0KICAgICAgICAgICAgICAgIHdpZHRoID0gMC4yLCBjb2xvdXIgPSAiI0QyMUYzQyIpICsNCiAgZ2VvbV9lcnJvcmJhcmgoZGF0YSA9IHVyYmluYV9ldF9hbF8yMDEyLCANCiAgICAgICAgICAgICAgICAgYWVzKHkgPSBtbzJfbWVhbiwgeG1pbiA9IG8yX21lYW4gLSBvMl9zZCwgeG1heCA9IG8yX21lYW4gKyBvMl9zZCksIA0KICAgICAgICAgICAgICAgICBoZWlnaHQgPSAwLjQsIGNvbG91ciA9ICIjRDIxRjNDIikgKw0KICBhbm5vdGF0ZSgidGV4dCIsIHggPSAwLCANCiAgICAgICAgICAgeSA9IDE2LCANCiAgICAgICAgICAgbGFiZWwgPSBicXVvdGUoYXRvcCgiQ3VycmVudCBkYXRhIChibHVlKSwgIiAqIGl0YWxpYyhuKSAqICIgPSAiICogLihuKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICJVcmJpbmEgZGF0YSAocmVkKSwgIiAqIGl0YWxpYyhuKSAqICIgPSA2NyIpKSwgIA0KICAgICAgICAgaGp1c3QgPSAwLCB2anVzdCA9IDEsIHNpemUgPSA0KSArDQogIHRoZW1lX2NsZWFuKCkgKw0KICBsYWJzKA0KICAgIHN1YnRpdGxlID0gIiIsDQogICAgeCA9ICJQTzIgKGtQYSkiLA0KICAgIHkgPSAiTU8yICh1bW9sIE8yIGcvaCkiDQogICkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCAxNiksIGJyZWFrcyA9IHNlcSgwLCAxNiwgYnkgPSAyKSkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCAyMiksIGJyZWFrcyA9IHNlcSgwLCAyMiwgYnkgPSAyKSkgIA0KDQpwDQpgYGANCg0KKipGaWd1cmUgUzE6KiogTWV0YWJvbGljIHJhdGUgKE1PPHN1Yj4yPC9zdWI+KSBieSBveHlnZW4gY29uY2VudHJhdGlvbiBmb3IgTzxzdWI+Mjwvc3ViPiBiaW5zIHdpdGggVXJiaW5hICgyMDEyKV5bMV1eICANCg0KIyMgUm91dGluZSBNTzxzdWI+Mjwvc3ViPg0KDQpNYWtpbmcgYW4gU01SIHBoYXNlIG9ubHkgZGF0YSBmcmFtZSAgIA0KDQpgYGB7cn0NCnNsb3BlX3RpZHlfc21yIDwtIHNsb3BlX3RpZHkgJT4lIA0KICBkcGx5cjo6ZmlsdGVyKHBoYXNlID09ICJzbXIiKQ0KYGBgDQoNCiMjIyBGaWd1cmUgU2k6IFJvdXRpbmUgTU88c3ViPjI8L3N1Yj4gYnkgc2FpbGl0eSANCg0KYGBge3J9DQptZWFuX21vMl9zYWxpbml0eSA8LSBzbG9wZV90aWR5X3NtciAlPiUgDQogIGRwbHlyOjpncm91cF9ieShzYWxpbml0eV9ncm91cCkgJT4lIA0KICBkcGx5cjo6cmVmcmFtZShtZWFuX21vMiA9IG1lYW4oTU8yLCBuYS5ybSA9IFRSVUUpKQ0KDQpmaWdfaSA8LSBnZ3Bsb3QoKSArDQogICAgZ2VvbV92aW9saW4oZGF0YSA9IHNsb3BlX3RpZHlfc21yLCBhZXMoeCA9IHNhbGluaXR5X2dyb3VwLCB5ID0gTU8yLCBmaWxsID0gc2FsaW5pdHlfZ3JvdXApLCBjb2xvciA9IE5BLCBhbHBoYSA9IDAuMykgKw0KICBnZW9tX2ppdHRlcihkYXRhID0gc2xvcGVfdGlkeV9zbXIsIGFlcyh4ID0gc2FsaW5pdHlfZ3JvdXAsIHkgPSBNTzIsIGZpbGwgPSBzYWxpbml0eV9ncm91cCksDQogICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gMjEsIHNpemUgPSAyLCBjb2xvciA9ICJibGFjayIsIGFscGhhID0gMC4yKSArDQogIGdlb21fYm94cGxvdChkYXRhID0gc2xvcGVfdGlkeV9zbXIsIGFlcyh4ID0gc2FsaW5pdHlfZ3JvdXAsIHkgPSBNTzIsIGZpbGwgPSBzYWxpbml0eV9ncm91cCksDQogICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMSwgYWxwaGEgPSAwLjUsIG91dGxpZXIuc2hhcGUgPSBOQSwgd2lkdGggPSAwLjMpICsNCiAgZ2VvbV9wb2ludChkYXRhID0gbWVhbl9tbzJfc2FsaW5pdHksIA0KICAgICAgICAgICAgICAgIGFlcyh4ID0gc2FsaW5pdHlfZ3JvdXAsIHkgPSBtZWFuX21vMiwgZmlsbCA9IHNhbGluaXR5X2dyb3VwKSwgDQogICAgICAgICAgICAgICAgc2l6ZSA9IDMsIGFscGhhID0gMC44LCBjb2xvdXIgPSAiYmxhY2siLCBzdHJva2UgPSAyKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiM0QjUzMjAiLCAiIzAwMDA4MCIpKSArICAjIEN1c3RvbSBmaWxsIGNvbG91cnMNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKCIjNEI1MzIwIiwgIiMwMDAwODAiKSkgKw0KICB0aGVtZV9jbGVhbigpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIGxhYnMoDQogICAgc3VidGl0bGUgPSAiIiwNCiAgICB4ID0gIlNhbGluaXR5IGdyb3VwIChwcHQpIiwNCiAgICB5ID0gIlJvdXRpbmUgTU8yIChtZyBPMiBnL2gpIg0KICApDQoNCmZpZ19pDQpgYGANCg0KKipGaWd1cmUgU2k6KiogUGxvdCBvZiBhbGwgTU88c3ViPjI8L3N1Yj4gbWVhc3VyZXMgZHVyaW5nIFNNUiBwaGFzZSBieSBzYWxpbml0eSB0cmVhdG1lbnQuIFRoZSBzbWFsbCBwb2ludHMgYXJlIHRoZSBvYnNlcnZlZCB2YWx1ZXMsIHRoZSBzaGFkZWQgYXJlYSBiZWhpbmQgdGhlIHBvaW50cyBpcyB0aGUgYSBrZXJuZWwgZGVuc2l0eSBvZiB0aGUgb2JzZXJ2ZWQgZGF0YSwgdGhlIGJveCBwbG90IG9uIHRvcCBzaG93IHRoZSBtZWRpYW4gYW5kIGludGVycXVhcnRpbGUgcmFuZ2UgKElRUikuIA0KDQojIyBTTVINCg0KIyMjIEZpZ3VyZSBTaTogU01SIGJ5IHNhaWxpdHkgDQoNCkhlcmUncyB0aGUgc2FtZSBwbG90IGJ1dCBmb3Igb25seSB0aGUgU01SLCBhcyBlc3RpbWF0ZWQgd2l0aCBjYWxjU01SIGZ1bmN0aW9uIGJ5IENoYWJvdCwgU3RlZmZlbnNlbiBhbmQgRmFycmVsbCAoMjAxNileWzFdXi4gU3BlY2lmaWNhbGx5LCB3ZSB1c2UgbWVhbiBvZiB0aGUgbG93ZXN0IG5vcm1hbCBkaXN0cmlidXRpb24gKE1MTkQpIHdoZXJlIENWbWxuZCA8IDUuNCBhbmQgdGhlIG1lYW4gb2YgdGhlIGxvd2VyIDIwJSBxdWFudGlsZSAocTAuMikgd2VyZSBDVm1sbmQgPiA1LjQuIElmIENWbWxuZCBpcyBub3QgY2FsY3VsYXRlZCB3ZSBoYXZlIHVzZWQgcTAuMi4gICANCg0KYGBge3J9DQpzbXJfb25seSA8LSBzbG9wZV90aWR5X3NtciAlPiUgDQogIGRwbHlyOjpncm91cF9ieShpZCkgJT4lIA0KICBkcGx5cjo6c2xpY2UoMSkNCg0KbWVhbl9zbXJfc2FsaW5pdHkgPC0gc21yX29ubHkgJT4lIA0KICBkcGx5cjo6Z3JvdXBfYnkoc2FsaW5pdHlfZ3JvdXApICU+JSANCiAgZHBseXI6OnJlZnJhbWUobWVhbl9zbXIgPSBtZWFuKFNNUiwgbmEucm0gPSBUUlVFKSkNCg0KZmlnX2kgPC0gZ2dwbG90KCkgKw0KICAgIGdlb21fdmlvbGluKGRhdGEgPSBzbXJfb25seSwgYWVzKHggPSBzYWxpbml0eV9ncm91cCwgeSA9IFNNUiwgZmlsbCA9IHNhbGluaXR5X2dyb3VwKSwgY29sb3IgPSBOQSwgYWxwaGEgPSAwLjMpICsNCiAgZ2VvbV9qaXR0ZXIoZGF0YSA9IHNtcl9vbmx5LCBhZXMoeCA9IHNhbGluaXR5X2dyb3VwLCB5ID0gU01SLCBmaWxsID0gc2FsaW5pdHlfZ3JvdXApLA0KICAgICAgICAgICAgICAgICAgICAgICBzaGFwZSA9IDIxLCBzaXplID0gMiwgY29sb3IgPSAiYmxhY2siLCBhbHBoYSA9IDAuMikgKw0KICBnZW9tX2JveHBsb3QoZGF0YSA9IHNtcl9vbmx5LCBhZXMoeCA9IHNhbGluaXR5X2dyb3VwLCB5ID0gU01SLCBmaWxsID0gc2FsaW5pdHlfZ3JvdXApLA0KICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDEsIGFscGhhID0gMC41LCBvdXRsaWVyLnNoYXBlID0gTkEsIHdpZHRoID0gMC4zKSArDQogIGdlb21fcG9pbnQoZGF0YSA9IG1lYW5fc21yX3NhbGluaXR5LCANCiAgICAgICAgICAgICAgICBhZXMoeCA9IHNhbGluaXR5X2dyb3VwLCB5ID0gbWVhbl9zbXIsIGZpbGwgPSBzYWxpbml0eV9ncm91cCksIA0KICAgICAgICAgICAgICAgIHNpemUgPSAzLCBhbHBoYSA9IDAuOCwgY29sb3VyID0gImJsYWNrIiwgc3Ryb2tlID0gMikgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjNEI1MzIwIiwgIiMwMDAwODAiKSkgKyAgIyBDdXN0b20gZmlsbCBjb2xvdXJzDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiIzRCNTMyMCIsICIjMDAwMDgwIikpICsNCiAgdGhlbWVfY2xlYW4oKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICBsYWJzKA0KICAgIHN1YnRpdGxlID0gIiIsDQogICAgeCA9ICJTYWxpbml0eSBncm91cCAocHB0KSIsDQogICAgeSA9ICJNTzIgKG1nIE8yIGcvaCkiDQogICkNCg0KZmlnX2kNCmBgYA0KDQojIyMgRmlndXJlIFNpOiBBbGwgZmlzaCBTTVIgYW5kIGNsb3NlZCBNTzxzdWI+Mjwvc3ViPiB2YXVsZXMNCg0KQ3JlYXRlIG91dHB1dCBkaXJlY3RvcnkgaWYgbmVlZGVkICANCg0KYGBge3IsIGVjaG89RkFMU0V9DQojIENyZWF0ZSBvdXRwdXQgZGlyZWN0b3J5IGlmIG5lZWRlZA0Kb3V0cHV0X2ZpZ19zbG9wZXNfd2QgPC0gZmlsZS5wYXRoKG91dHB1dF9maWdfd2QsICJzbG9wZXMiKQ0KaWYgKCFkaXIuZXhpc3RzKG91dHB1dF9maWdfc2xvcGVzX3dkKSkgew0KICBkaXIuY3JlYXRlKG91dHB1dF9maWdfc2xvcGVzX3dkKQ0KfQ0KYGBgDQoNCg0KYGBge3IsIGZpZy5oZWlnaHQgPSAzLCBmaWcud2lkdGg9IDR9DQppZHMgPC0gc2xvcGVfdGlkeSAlPiUgDQogIGRwbHlyOjpkaXN0aW5jdChpZCkgJT4lIA0KICBwdWxsKGlkKSAlPiUgDQogIGFzLmxpc3QoKQ0KDQpNTzJfcGxvdF9saXN0IDwtICBsaXN0KCkNCg0KIyAxKSBPcGVuIHRoZSBQREYgZGV2aWNlIG9uY2UNCnBkZigNCiAgZmlsZSAgID0gZmlsZS5wYXRoKG91dHB1dF9maWdfc2xvcGVzX3dkLCAiY29tYmluZWRfc2xvcGVzLnBkZiIpLCANCiAgd2lkdGggID0gOCwgDQogIGhlaWdodCA9IDYNCikNCg0KIyAyKSBMb29wIG92ZXIgSURzIGFuZCBjcmVhdGUgZWFjaCBwbG90DQpmb3IgKGlkX2kgaW4gaWRzKSB7DQogIA0KICBzbXIgPC0gc2xvcGVfdGlkeSAlPiUgDQogICAgZHBseXI6OmZpbHRlcihpZCA9PSBpZF9pKSAlPiUgDQogICAgZHBseXI6OnNsaWNlKDEpICU+JSANCiAgICBkcGx5cjo6cHVsbChTTVIpDQogIA0KICBwbG90IDwtIHNsb3BlX3RpZHkgJT4lIA0KICAgIGRwbHlyOjpmaWx0ZXIoaWQgPT0gaWRfaSkgJT4lIA0KICAgIGdncGxvdChhZXMoeCA9IG8yLCB5ID0gTU8yKSkgKw0KICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IHNtciwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZGFya3JlZCIpICsNCiAgICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBwaGFzZSkpICsNCiAgICB0aGVtZV9jbGVhbigpICsNCiAgICBsYWJzKA0KICAgICAgc3VidGl0bGUgPSBwYXN0ZTAoaWRfaSwgIiBzbG9wZXMiKSwNCiAgICAgIHggPSAiTWVhbiBvMiAobWdfcGVyX2wpIiwNCiAgICAgIHkgPSAiYWJzKG1vMikgKG1nX3Blcl9sKSINCiAgICApDQogIA0KICAjIEluc3RlYWQgb2Ygc2F2aW5nIGVhY2ggcGxvdCBzZXBhcmF0ZWx5LCBqdXN0IHByaW50IGl0DQogIHByaW50KHBsb3QpDQogIA0KICBNTzJfcGxvdF9saXN0W1tpZF9pXV0gPC0gcGxvdA0KfQ0KDQojIDMpIENsb3NlIHRoZSBQREYgZGV2aWNlICphZnRlciogdGhlIGxvb3ANCmRldi5vZmYoKQ0KDQpmb3IgKHAgaW4gTU8yX3Bsb3RfbGlzdCkgew0KICBwcmludChwKQ0KfQ0KYGBgDQoNCioqRmlndXJlIFNpKio6IE1PPHN1Yj4yPHN1Yi8+IGJ5IE88c3ViPjI8L3N1Yj4gZm9yIGVhY2ggZmlzaCB3aXRoIHRoZSBTTVIgcmVwcmVzZW50ZWQgYXMgYSBkYXNoZWQgcmVkIA0KDQo8IS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0+DQojIEFuYWx5c2lzDQo8IS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0+DQoNCiMjIFJvdXRpbmUgTU8yDQoNCiMjIyBTY2FsaW5nIHByZWRpY3RvcnMNCg0KSGVyZSB3ZSBzY2FsZSBvdXIgcHJlZGljdG9ycyBmb3IgdGhlIG1vZGVsICAgDQoNCmBgYHtyfQ0Kc2NhbGVfbGlzdCA8LSBjKCJ0ZW1wIiwgIm9yZGVyIiwgIm1hc3MiKSANCg0Kc2xvcGVfdGlkeV9zbXIgPC0gc2xvcGVfdGlkeV9zbXIgJT4lDQogIGRwbHlyOjptdXRhdGUoYWNyb3NzKGFsbF9vZihzY2FsZV9saXN0KSwgfiBzY2FsZSgueCwgY2VudGVyID0gVFJVRSwgc2NhbGUgPSBGQUxTRSksDQogICAgICAgICAgICAgICAgICAgICAgIC5uYW1lcyA9ICJ7LmNvbH1feiIpLA0KICAgICAgICAgICAgICAgIGxpZ2h0X2RhcmtfYyA9IGlmX2Vsc2UobGlnaHRfZGFyayA9PSAibGlnaHQiLCAwLjUsIC0wLjUpKQ0KYGBgDQoNCiMjIyBNb2RlbCBzdHJ1Y3R1cmUNCg0KSGVyZSB3ZSB3aWxsIHVzZSBhIEJheWVzaWFuIEdlbmVyYWxpc2VkIExpbmVhciBNaXhlZCBNb2RlbCAoR0xNTSkgd2l0aCBhIEdhbW1hIGRpc3RyaWJ1dGlvbiBhbmQgYSBsb2cgbGluaywgd2hlcmUgdGhlIHNoYXBlIHBhcmFtZXRlciAoSykgaXMgYWxzbyBtb2RlbGxlZCBhcyBhIGZ1bmN0aW9uIG9mIHByZWRpY3RvcnMuIFRoaXMgbW9kZWxzIE1PPHN1Yj4yPC9zdWI+IGJ5IHNhbGluaXR5IGR1cmluZyB0aGUgU01SIHBoYXNlIHRvIHNlZSBpZiB0aGUgZmlzaCBoZWxkIGF0IGRpZmZlcmVudCBzYWxpbml0aWVzIGhhdmUgZGlmZmVyZW50IFNNUnMuIFdlIGhhdmUgYWxzbyBhZGRlZCBhIGZldyBzY2FsZWQgcHJlZGljdG9ycywgdGhhdCBtYXkgaGVscCBkZXNjcmliZSB2YXJpYXRpb24gaW4gdGhlIGRhdGEsIHN1Y2ggYXMgbWFzcyAoZzsgMC4yMeKAkzEuNikgdGVtcGVyYXR1cmUgKMKwQzsgMTMuODQx4oCTMTQuMjc3KSwgbWVhc3VyZW1lbnQgb3JkZXIgKDHigJMyOCksIGFuZCBsaWdodC9kYXJrIGN5Y2xlIChsaWdodCBvciBkYXJrOyBsaWdodCBiZXR3ZWVuIDA3OjAwOjAwIGFuZCAxOTowMDowMCksIHdlIGFsc28gaW5jbHVkZSBhIHJhbmRvbSBlZmZlY3QgZm9yIGZpc2ggaWQgdG8gYWNjb3VudCBmb3IgbXVsdGlwbGUgTU88c3ViPjI8L3N1Yj4gbWVhc3VyZXMgb24gZWFjaCBmaXNoICgxIC0gNTgpLiBXZSBhbGxvd2VkIHRoZSB0aGUgc2hhcGUgcGFyYW1ldGVyIChLKSB0byB2YXJ5IGFzIGEgZnVuY3Rpb24gb2Ygc29tZSBvZiB0aGUgcHJlZGljdG9ycyAoZS5nLiBzYWxpbml0eV9ncm91cCwgb3JkZXJfeikgdG8gaW1wcm92ZSBmaXQuICAgDQoNCmBgYHtyfQ0KbW8yX2dhbW1hX2JmIDwtIGJmKE1PMiB+IHRlbXBfeiArIA0KICAgICAgICAgICAgICAgICAgb3JkZXJfeiAgKyANCiAgICAgICAgICAgICAgICAgIGxpZ2h0X2RhcmtfYyArIA0KICAgICAgICAgICAgICAgICAgbWFzc196ICsgDQogICAgICAgICAgICAgICAgICBzYWxpbml0eV9ncm91cCArICgxfGlkKSwNCiAgICAgICAgICAgICAgICBzaGFwZSB+IHNhbGluaXR5X2dyb3VwICsgDQogICAgICAgICAgICAgICAgICBvcmRlcl96LA0KICAgICAgICAgICAgICAgIGZhbWlseSA9IEdhbW1hKGxpbmsgPSAibG9nIikpDQpgYGANCg0KDQojIyMgUHJpb3Igc2VsZWN0aW9uDQoNClRoZXNlIGFyZSB0aGUgZGVmYXVsdCBwcmlvcnMuIFdlIHdpbGwgdXNlIHRoZXNlLg0KDQpgYGB7cn0NCmRlZmF1bHRfcHJpb3IgPC0gZ2V0X3ByaW9yKG1vMl9nYW1tYV9iZiwgZGF0YSA9IHNsb3BlX3RpZHlfc21yLCBmYW1pbHkgPSBHYW1tYShsaW5rID0gImxvZyIpKQ0KZGVmYXVsdF9wcmlvcg0KYGBgDQoNClVzaW5nIGdlbmVyaWMgc3RhbmRhcmRpc2VkIHdlYWtseSBpbmZvcm1hdGl2ZSBwcmlvcnMgZm9yIGFsbCBlc3RpbWF0ZXMgKGIpDQoNCmBgYHtyfQ0KcHJpb3JzX2N1c3RvbSA8LSBjKA0KICBzZXRfcHJpb3IoIm5vcm1hbCgwLCAxKSIsIGNsYXNzID0gImIiKSwNCiAgc2V0X3ByaW9yKCJzdHVkZW50X3QoMywgMCwgMTApIiwgY2xhc3MgPSAiYiIsIGRwYXIgPSAic2hhcGUiKSwNCiAgc2V0X3ByaW9yKCJzdHVkZW50X3QoMywgMCwgMi41KSIsIGNsYXNzID0gInNkIiksDQogIHNldF9wcmlvcigic3R1ZGVudF90KDMsIC0yLjgsIDIuNSkiLCBjbGFzcyA9ICJJbnRlcmNlcHQiKSwNCiAgc2V0X3ByaW9yKCJzdHVkZW50X3QoMywgMCwgMi41KSIsIGNsYXNzID0gIkludGVyY2VwdCIsIGRwYXIgPSAic2hhcGUiKQ0KKQ0KcHJpb3JzX2N1c3RvbQ0KYGBgDQoNCiMjIyBSdW4gbW9kZWwNCg0KSGVyZSB3ZSBydW4gdGhlIG1vZGVsLCBJIGhhdmUgaGFzaGVkIHRoaXMgb3V0IGJlY2F1c2UgSSBoYXZlIHNhdmVkIHRoZSBtb2RlbCBmb3IgcXVpY2sgcmVsb2FkaW5nLiANCg0KYGBge3IsIGVjaG89RkFMU0V9DQpzZXR3ZChvdXRwdXRfbW9kc193ZCkNCg0KbW8yX21vZF9nYW1tYSA8LSBicm0obW8yX2dhbW1hX2JmLA0KICAgICAgICAgICAgICAgZGF0YSA9IHNsb3BlX3RpZHlfc21yLA0KICAgICAgICAgICAgICAgY29yZXMgPSA0LA0KICAgICAgICAgICAgICAgY2hhaW5zID0gNCwNCiAgICAgICAgICAgICAgIHByaW9yID0gZGVmYXVsdF9wcmlvciwNCiAgICAgICAgICAgICAgIHdhcm11cCA9IDEwMDAsDQogICAgICAgICAgICAgICBzZWVkID0gMTQzMDE5LA0KICAgICAgICAgICAgICAgdGhpbiA9IDIsDQogICAgICAgICAgICAgICBpdGVyID0gODAwMCwNCiAgICAgICAgICAgICAgIHNhdmVfcGFycyA9IHNhdmVfcGFycyhhbGw9VFJVRSksDQogICAgICAgICAgICAgICBzYW1wbGVfcHJpb3IgPSBUUlVFLA0KICAgICAgICAgICAgICAgZmlsZSA9ICdtbzJfbW9kX2dhbW1hJykNCnByaW50KCJNb2RlbCBjb21wbGV0ZSIpDQpgYGANCg0KUHJpb3Igb25seSBtb2RlbA0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCiMgc2V0d2Qob3V0cHV0X21vZHNfd2QpDQojIA0KIyBtbzJfbW9kX2dhbW1hX3ByaW9yIDwtIGJybShtbzJfZ2FtbWFfYmYsDQojICAgICAgICAgICAgICAgIGRhdGEgPSBzbG9wZV90aWR5X3NtciwNCiMgICAgICAgICAgICAgICAgY29yZXMgPSA0LA0KIyAgICAgICAgICAgICAgICBjaGFpbnMgPSA0LA0KIyAgICAgICAgICAgICAgICBwcmlvciA9IGRlZmF1bHRfcHJpb3IsDQojICAgICAgICAgICAgICAgIHdhcm11cCA9IDEwMDAsDQojICAgICAgICAgICAgICAgIHNlZWQgPSAxNDMwMTksDQojICAgICAgICAgICAgICAgIHRoaW4gPSAyLA0KIyAgICAgICAgICAgICAgICBpdGVyID0gODAwMCwNCiMgICAgICAgICAgICAgICAgc2F2ZV9wYXJzID0gc2F2ZV9wYXJzKGFsbD1UUlVFKSwNCiMgICAgICAgICAgICAgICAgc2FtcGxlX3ByaW9yID0gIm9ubHkiLA0KIyAgICAgICAgICAgICAgICBmaWxlID0gJ21vMl9tb2RfZ2FtbWFfcHJpb3InKQ0KIyBwcmludCgiTW9kZWwgY29tcGxldGUiKQ0KYGBgDQoNCkhlcmUgd2UgcmVsb2FkIHRoZSBtb2RlbCAgIA0KDQpgYGB7cn0NCnNldHdkKG91dHB1dF9tb2RzX3dkKQ0KDQptbzJfbW9kX2dhbW1hIDwtICByZWFkUkRTKGZpbGUgPSAibW8yX21vZF9nYW1tYS5yZHMiKQ0KI21vMl9tb2RfZ2FtbWFfcHJpb3IgPC0gIHJlYWRSRFMoZmlsZSA9ICJtbzJfbW9kX2dhbW1hX3ByaW9yLnJkcyIpDQpgYGANCg0KIyMjIE1vZGVsIGRpYWdub3N0aWNzDQoNCkNoZWNraW5nIG1vZGVsIGNvbnZlcmdlbmNlICAgDQoNCmBgYHtyfQ0KY29sb3Jfc2NoZW1lX3NldCgicmVkIikNCnBsb3QobW8yX21vZF9nYW1tYSwgYXNrID0gRikNCmBgYA0KDQpDaGVja2luZyByaGF0IGFyZSBlcXVhbCB0byBvbmUgICANCg0KYGBge3J9DQpzdW1tYXJ5KG1vMl9tb2RfZ2FtbWEpDQpgYGANCg0KTG9va2luZyBhdCBwcmlvciBhbmQgcHJlZGljdGVkIGRpc3RyaWJ1dGlvbnMgZm9yIG91ciBwYXJhbWV0ZXJzICAgDQoNCmBgYHtyfQ0KIyBrZWVwX3BhcmFtZXRlcnMgPC0gIGMoImJfSW50ZXJjZXB0IiwgImJfc2hhcGVfSW50ZXJjZXB0IiwgImJfdGVtcF96IiwgImJfb3JkZXJfeiIsICJiX2xpZ2h0X2RhcmtfYyIsICJiX21hc3NfeiIsICJiX3NhbGluaXR5X2dyb3VwOSIsICJiX3NoYXBlX3NhbGluaXR5X2dyb3VwOSIpDQojIA0KIyBwcmlvcl9zYW1wbGVzIDwtIGFzX2RyYXdzX2RmKG1vMl9tb2RfZ2FtbWFfcHJpb3JfMikNCiMgcG9zdGVyaW9yX3NhbXBsZXMgPC0gYXNfZHJhd3NfZGYobW8yX21vZF9nYW1tYV8yKQ0KIyANCiMgcG9zdGVyaW9yX3NhbXBsZXMkc291cmNlIDwtICJQb3N0ZXJpb3IiDQojIHByaW9yX3NhbXBsZXMkc291cmNlIDwtICJQcmlvciINCiMgDQojIGNvbWJpbmVkX3NhbXBsZXMgPC0gIGJpbmRfcm93cyhwb3N0ZXJpb3Jfc2FtcGxlcywgcHJpb3Jfc2FtcGxlcykNCiMgDQojICMgQ29udmVydCB0byBsb25nIGZvcm1hdCBmb3IgZ2dwbG90DQojIGxvbmdfc2FtcGxlcyA8LSBwaXZvdF9sb25nZXIoY29tYmluZWRfc2FtcGxlcywgDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29scyA9IC1zb3VyY2UsIA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gInBhcmFtZXRlciIsIA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJ2YWx1ZSIpICU+JSANCiMgICBkcGx5cjo6ZmlsdGVyKHBhcmFtZXRlciAlaW4lIGtlZXBfcGFyYW1ldGVycykNCiMgDQojIGdncGxvdChsb25nX3NhbXBsZXMsIGFlcyh4ID0gdmFsdWUsIGZpbGwgPSBzb3VyY2UpKSArDQojICAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC41KSArICAjIE92ZXJsYXkgZGVuc2l0eSBwbG90cw0KIyAgIGZhY2V0X3dyYXAofnBhcmFtZXRlciwgc2NhbGVzID0gImZyZWUiKSArICAjIFNlcGFyYXRlIGJ5IHBhcmFtZXRlcg0KIyAgIGxhYnModGl0bGUgPSAiUHJpb3IgdnMgUG9zdGVyaW9yIERpc3RyaWJ1dGlvbnMiLCB4ID0gIlBhcmFtZXRlciBWYWx1ZSIsIHkgPSAiRGVuc2l0eSIpICsNCiMgICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQoNClVzaW5nIGxlYXZlIG9uZSBvdXQgKGxvbykgbWVhc3VyZSBvZiBmaXQsIHRoZSBtb2RlbCBhcHBlYXJzIHRvIHByZWZvcm0gd2VsbCwgYWxsIFBhcmV0byBrIGVzdGltYXRlcyBhcmUgZ29vZCAoayA8IDAuNykgICANCg0KYGBge3J9DQpsb28obW8yX21vZF9nYW1tYSkNCmBgYA0KDQpNb2RlbCBwcmVkaWN0aW9ucyBnZW5lcmFsbHkgYWxpZ24gd2l0aCB0aGUgb2JzZXJ2ZWQgZGF0YSAgIA0KDQpgYGB7cn0NCmNvbG9yX3NjaGVtZV9zZXQoInJlZCIpDQpwIDwtIHBwX2NoZWNrKG1vMl9tb2RfZ2FtbWEsIHR5cGUgPSAiZGVuc19vdmVybGF5IikNCnANCmBgYA0KDQoNCiMjIyBSZXN1bHRzDQoNCldlIGRpZCBub3Qgc2VlIGEgbWVhbmluZ2Z1bCBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIHJvdXRpbmUgbWV0YWJvbGljIHJhdGUgZm9yIGZpc2ggZnJvbSB0aGUgdHdvIHNhbGluaXR5IHRyZWF0bWVudHMuICAgDQoNCiMjIyMgVGFibGUgU2kgDQoNCioqVGFibGUgU2kqKjogRml4ZWQgZWZmZWN0IEVzdGltYXRlcyAozrIpIGFuZCA5NSUgQ3JlZGlibGUgSW50ZXJ2YWxzICg5NSUgQ0kpICAgDQoNCmBgYHtyfQ0KbW9kZWxfZXN0IDwtIGZpeGVmKG1vMl9tb2RfZ2FtbWEsIHByb2JzID0gYygwLjAyNSwgMC45NzUpKSAlPiUgDQogIGFzLmRhdGEuZnJhbWUoKSAlPiUgDQogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJQcmVkaWN0b3IiKSAlPiUgDQogIGRwbHlyOjptdXRhdGUoJ86yJyA9IHJvdW5kKEVzdGltYXRlLCAzKSwNCiAgICAgICAgICAgICAgICBRMi41ID0gcm91bmQoUTIuNSwgMyksDQogICAgICAgICAgICAgICAgUTk3LjUgPSByb3VuZChROTcuNSwgMyksDQogICAgICAgICAgICAgICAgJzk1JSBDSScgPSBwYXN0ZTAoIlsiLCBRMi41LCAiLCAiLCBROTcuNSwgIl0iKSkNCg0KbW9kZWxfZXN0ICU+JSANCiAgZHBseXI6OnNlbGVjdChQcmVkaWN0b3IsICfOsicsICc5NSUgQ0knKSAlPiUgDQogIGd0KCkNCmBgYA0KICANCkxvb2tpbmcgYXQgdGhlIG1hcmdpbmFsIG1lYW4gZGlmZmVyZW5jZSBiZXR3ZWVuIHNhbGluaXR5IGdyb3VwcyAgIA0KDQpgYGB7cn0NCmVtX3Jlc3VsdHMgPC0gZW1tZWFucyhtbzJfbW9kX2dhbW1hLCB+IHNhbGluaXR5X2dyb3VwKSANCmNvbnRyYXN0X3Jlc3VsdHMgPC0gY29udHJhc3QoZW1fcmVzdWx0cywgbWV0aG9kID0gInBhaXJ3aXNlIikNCmVtX3Jlc3VsdHNfZGYgPC0gIGVtX3Jlc3VsdHMgJT4lIHRpZHkoKSAlPiUgDQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYyksIH4gZXhwKC4pKSkNCmNvbnRyYXN0X3Jlc3VsdHNfZGYgPC0gIGNvbnRyYXN0X3Jlc3VsdHMgJT4lIHRpZHkoKSAlPiUgDQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYyksIH4gZXhwKC4pKSkNCg0KDQplbV9yZXN1bHRzX2RmICU+JSANCiAgZ3QoKQ0KYGBgDQoNClB1bGxpbmcgdGhlIGVtbWVhbnMgZHJhd3MgZm9yIG91ciBwbG90ICAgDQoNCmBgYHtyfQ0KZW1tZWFuc19kcmF3cyA8LSBtbzJfbW9kX2dhbW1hICU+JQ0KICBlbW1lYW5zKH4gc2FsaW5pdHlfZ3JvdXApICU+JQ0KICBnYXRoZXJfZW1tZWFuc19kcmF3cygpICU+JSANCiAgZHBseXI6Om11dGF0ZSgudmFsdWUgPSAgZXhwKC52YWx1ZSksDQogICAgICAgICAgICAgICAgc2FsaW5pdHlfZ3JvdXAgPSBhcy5jaGFyYWN0ZXIoc2FsaW5pdHlfZ3JvdXApKQ0KDQplbW1lYW5zX2NvbnRyYXN0X2RyYXdzIDwtICBtbzJfbW9kX2dhbW1hICU+JQ0KICBlbW1lYW5zKH4gc2FsaW5pdHlfZ3JvdXApICU+JQ0KICBjb250cmFzdChtZXRob2QgPSAicGFpcndpc2UiKSAlPiUNCiAgZ2F0aGVyX2VtbWVhbnNfZHJhd3MoKSAlPiUgDQogIGRwbHlyOjptdXRhdGUoLnZhbHVlID0gIGV4cCgudmFsdWUpKQ0KYGBgDQoNCiMjIyMgRmlndXJlIFNpDQoNCioqKk5PVEU6IFRoaXMgcGxvdCBpcyBpbiB0aGUgbWFpbiB0ZXh0IG9mIHRoZSBtYW51c2NyaXB0IGFzIEZpZ3VyZSAxYSoqKg0KDQpgYGB7cn0NCm1lYW5fbW8yX3NhbGluaXR5IDwtIHNsb3BlX3RpZHlfc21yICU+JSANCiAgZHBseXI6Omdyb3VwX2J5KHNhbGluaXR5X2dyb3VwKSAlPiUgDQogIGRwbHlyOjpyZWZyYW1lKG1lYW5fbW8yID0gbWVhbihNTzIsIG5hLnJtID0gVFJVRSkpDQoNCg0KZmlnXzEgPC0gZ2dwbG90KCkgKw0KICBnZW9tX3Zpb2xpbihkYXRhID0gc2xvcGVfdGlkeV9zbXIsDQogICAgICAgICAgICAgIGFlcyh4ID0gc2FsaW5pdHlfZ3JvdXAsIHkgPSBNTzIsIGZpbGwgPSBzYWxpbml0eV9ncm91cCksDQogICAgICAgICAgICAgIGNvbG9yID0gTkEsIGFscGhhID0gMC4yKSArDQogIGdlb21faml0dGVyKGRhdGEgPSBzbG9wZV90aWR5X3NtciwNCiAgICAgICAgICAgICAgYWVzKHggPSBzYWxpbml0eV9ncm91cCwgeSA9IE1PMiwgZmlsbCA9IHNhbGluaXR5X2dyb3VwKSwNCiAgICAgICAgICAgICAgc2hhcGUgPSAyMSwgd2lkdGggPSAwLjMsIHNpemUgPSAxLCBjb2xvciA9ICJibGFjayIsIGFscGhhID0gMC4xKSArDQogICAgZ2VvbV9wb2ludChkYXRhID0gbWVhbl9tbzJfc2FsaW5pdHksDQogICAgICAgICAgICAgYWVzKHggPSBzYWxpbml0eV9ncm91cCwgeSA9IG1lYW5fbW8yLCBmaWxsID0gc2FsaW5pdHlfZ3JvdXApLA0KICAgICAgICAgICAgIHNpemUgPSA0LCBhbHBoYSA9IDEsIHN0cm9rZSA9IDIsIGNvbG9yID0gImJsYWNrIiwgc2hhcGUgPSAyMSwNCiAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh4ID0gMC4wNSkpICsNCiAgc3RhdF9wb2ludGludGVydmFsKGRhdGEgPSBlbW1lYW5zX2RyYXdzLCANCiAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gc2FsaW5pdHlfZ3JvdXAsIHkgPSAudmFsdWUpLA0KICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImdyZXkiLCBwb2ludF9pbnRlcnZhbCA9ICJtZWFuX3FpIiwgLndpZHRoID0gMC45NSwgc2hhcGUgPSAyMSwgIHN0cm9rZSA9IDIsIHBvaW50X3NpemUgPSA0LCBhbHBoYSA9IDEsDQogICAgICAgICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX251ZGdlKHggPSAtMC4wNSkpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiIzRCNTMyMCIsICIjMDAwMDgwIikpICsgICMgQ3VzdG9tIGZpbGwgY29sb3Vycw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoIiM0QjUzMjAiLCAiIzAwMDA4MCIpKSArDQogIHRoZW1lX2NsZWFuKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgbGFicygNCiAgICBzdWJ0aXRsZSA9ICIiLA0KICAgIHggPSAiU2FsaW5pdHkgZ3JvdXAgKHBwdCkiLA0KICAgIHkgPSAiUm91dGluZSBNTzIgKG1nIE8yIGcvaCkiDQogICkNCg0KZmlnXzENCmBgYA0KDQoqKkZpZ3VyZSAxYToqKiBSb3V0aW5lIG1ldGFib2xpYyByYXRlIChpLmUuIE1PPHN1Yj4yPC9zdWI+IChtZ14tMV4gTzxzdWI+Mjwvc3ViPiBoXi0xXikgbWVhc3VyZWQgZHVyaW5nIFNNUiBtZWFzc3VybWVudHMpIHBsb3R0ZWQgYnkgc2FsaW5pdHkgdHJlYXRtZW50LiBUaGUgc21hbGwgdHJhbnNwYXJlbnQgcG9pbnRzIGFyZSB0aGUgb2JzZXJ2ZWQgdmFsdWVzLCB0aGUgc2hhZGVkIGFyZWEgYmVoaW5kIHRoZSBwb2ludHMgaXMgdGhlIGEga2VybmVsIGRlbnNpdHkgb2YgdGhlIG9ic2VydmVkIGRhdGEsIHRoZSBsYXJnZSBjb2xvdXJlZCBwb2ludCAodG8gdGhlIHJpZ2h0KSBpcyB0aGUgb2JzZXJ2ZWQgbWVhbiwgdGhlIGxhcmdlIGdyZXkgcG9pbnQgd2l0aCBlcnJvciBiYXJzICh0byB0aGUgbGVmdCkgaXMgdGhlIG1vZGVsIGVzdGltYXRlZCBtYXJnaW5hbCBtZWFuIChlZW1lYW4pIDk1JSBDcmVkaWJsZSBJbnRlcnZhbHMgKDk1JSBDSSkuIA0KDQojIyBTTVINCg0KIyMjIEZvcm1hdGluZyBhbmQgc2NhbGluZyBkYXRhDQoNCkhlcmUgd2UgYXJlIGZpbHRlcmluZyB0aGUgZGF0YSBmcmFtZSB0byBoYXZlIG9ubHkgbWVhc3VyZSBwZXIgZmlzaCBmb3IgdGhlIFNNUiBlc3RpbWF0ZSAgIA0KDQpgYGB7cn0NCnNjYWxlX2xpc3QgPC0gYygidGVtcF9tZWFuIiwgIm1hc3MiLCAiY3ljbGVzIikNCg0Kc21yIDwtIHNsb3BlX3RpZHlfc21yICU+JQ0KICBkcGx5cjo6Z3JvdXBfYnkoaWQpICU+JSANCiAgZHBseXI6OnJlZnJhbWUodGVtcF9tZWFuID0gbWVhbih0ZW1wKSwNCiAgICAgICAgICAgICAgICAgbWFzcyA9IG1hc3NbMV0sDQogICAgICAgICAgICAgICAgIFNNUiA9IFNNUlsxXSwNCiAgICAgICAgICAgICAgICAgc2FsaW5pdHlfZ3JvdXAgPSBzYWxpbml0eV9ncm91cFsxXSwNCiAgICAgICAgICAgICAgICAgY3ljbGVzID0gbGVuZ3RoKG9yZGVyKSkgJT4lIA0KICBkcGx5cjo6bXV0YXRlKGFjcm9zcyhhbGxfb2Yoc2NhbGVfbGlzdCksIH4gc2NhbGUoLngsIGNlbnRlciA9IFRSVUUsIHNjYWxlID0gRkFMU0UpLA0KICAgICAgICAgICAgICAgICAgICAgICAubmFtZXMgPSAiey5jb2x9X3oiKSkNCmBgYA0KDQojIyMgTW9kZWwgc3RydWN0dXJlDQoNCkhlcmUgd2Ugd2lsbCB1c2UgYSBCYXllc2lhbiBHZW5lcmFsaXNlZCBMaW5lYXIgTWl4ZWQgTW9kZWwgKEdMTU0pIHdpdGggYSBHYW1tYSBkaXN0cmlidXRpb24gYW5kIGEgbG9nIGxpbmsgYEdhbW1hKGxpbmsgPSAibG9nIilgLCB3aGVyZSB0aGUgc2hhcGUgcGFyYW1ldGVyIChLKSBpcyBhbHNvIG1vZGVsbGVkIGFzIGEgZnVuY3Rpb24gb2YgdGhlIHNhbGluaXR5IGdyb3VwLCBgc2hhcGUgfiBzYWxpbml0eV9ncm91cGAuIFRoaXMgbW9kZWxzIE1PMiBieSBzYWxpbml0eSBkdXJpbmcgdGhlIFNNUiBwaGFzZSB0byBzZWUgaWYgdGhlIGZpc2ggaGVsZCBhdCBkaWZmZXJlbnQgc2FsaW5pdGllcyBoYXZlIGRpZmZlcmVudCBTTVJzLiBXZSBoYXZlIGFsc28gYWRkZWQgYSBmZXcgc2NhbGVkIHByZWRpY3RvcnMsIHRoYXQgbWF5IGhlbHAgZGVzY3JpYmUgdmFyaWF0aW9uIGluIHRoZSBkYXRhLCBzdWNoIGFzIG1hc3MgKGc7IDAuMjHigJMxLjYpIHRlbXBlcmF0dXJlICjCsEM7IDEzLjg0MeKAkzE0LjI3NyksIG1lYXN1cmVtZW50IG9yZGVyICgx4oCTMjgpLCBhbmQgbGlnaHQvZGFyayBjeWNsZSAobGlnaHQgb3IgZGFyazsgbGlnaHQgYmV0d2VlbiAwNzowMDowMCBhbmQgMTk6MDA6MDApLCB3ZSBhbHNvIGluY2x1ZGUgYSByYW5kb20gZWZmZWN0IGZvciBmaXNoIGlkIHRvIGFjY291bnQgZm9yIG11bHRpcGxlIE1PMiBtZWFzdXJlcyBvbiBlYWNoIGZpc2ggKDEgLSA1OCkuIFdlIGFsbG93ZWQgdGhlIHRoZSBzaGFwZSBwYXJhbWV0ZXIgKEspIHRvIHZhcnkgYXMgYSBmdW5jdGlvbiBvZiBzb21lIG9mIHRoZSBwcmVkaWN0b3JzIChlLmcuIHNhbGluaXR5X2dyb3VwLCBvcmRlcl96KSB0byBpbXByb3ZlIGZpdC4gICANCg0KYGBge3J9DQpzbXJfZ2FtbWFfYmYgPC0gYmYoU01SIH4gdGVtcF9tZWFuX3ogKyANCiAgICAgICAgICAgICAgICAgIGN5Y2xlc196ICArDQogICAgICAgICAgICAgICAgICBtYXNzX3ogKyANCiAgICAgICAgICAgICAgICAgIHNhbGluaXR5X2dyb3VwLA0KICAgICAgICAgICAgICAgICAgc2hhcGUgfiBzYWxpbml0eV9ncm91cCwNCiAgICAgICAgICAgICAgICAgIGZhbWlseSA9IEdhbW1hKGxpbmsgPSAibG9nIikpDQpgYGANCg0KDQojIyMgUHJpb3Igc2VsZWN0aW9uDQoNClRoZXNlIGFyZSB0aGUgZGVmYXVsdCBwcmlvcnMgZm9yIHRoZSBtb2RlbC4gV2Ugd2lsbCB1c2UgdGhlc2UuDQoNCmBgYHtyfQ0KcHJpb3JzX2RlZmF1bHQgPC0gZ2V0X3ByaW9yKHNtcl9nYW1tYV9iZiwgZGF0YSA9IHNtciwgZmFtaWx5ID0gR2FtbWEobGluayA9ICJsb2ciKSkNCnByaW9yc19kZWZhdWx0DQpgYGANCg0KVGhlc2UgYXJlIGN1c3RvbSBnZW5lcmljIHN0YW5kYXJkaXNlZCB3ZWFrbHkgaW5mb3JtYXRpdmUgcHJpb3JzLCBmaXQgZm9yIGFsbCDOsiBlc3RpbWF0ZXMgICANCg0KYGBge3J9DQpwcmlvcnNfY3VzdG9tIDwtIGMoDQogIHNldF9wcmlvcigibm9ybWFsKDAsIDEpIiwgY2xhc3MgPSAiYiIpLCAgIyBQcmlvcnMgZm9yIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzDQogIHNldF9wcmlvcigic3R1ZGVudF90KDMsIC0yLjgsIDIuNSkiLCBjbGFzcyA9ICJJbnRlcmNlcHQiKSwgICMgUHJpb3IgZm9yIHRoZSBpbnRlcmNlcHQNCiAgc2V0X3ByaW9yKCJub3JtYWwoMCwgMSkiLCBjbGFzcyA9ICJiIiwgZHBhciA9ICJzaGFwZSIpLCAgIyBQcmlvcnMgZm9yIHNoYXBlIHBhcmFtZXRlciBjb2VmZmljaWVudHMNCiAgc2V0X3ByaW9yKCJzdHVkZW50X3QoMywgMCwgMi41KSIsIGNsYXNzID0gIkludGVyY2VwdCIsIGRwYXIgPSAic2hhcGUiKSAgIyBQcmlvciBmb3Igc2hhcGUgaW50ZXJjZXB0DQopDQpwcmlvcnNfY3VzdG9tDQpgYGANCg0KIyMjIFJ1biBtb2RlbA0KDQpIZXJlIHdlIHJ1biB0aGUgbW9kZWwsIEkgaGF2ZSBoYXNoZWQgdGhpcyBvdXQgYmVjYXVzZSBJIGhhdmUgc2F2ZWQgdGhlIG1vZGVsIGZvciBxdWljayByZWxvYWRpbmcuICAgDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0Kc2V0d2Qob3V0cHV0X21vZHNfd2QpDQoNCnNtcl9tb2RfZ2FtbWEgPC0gYnJtKHNtcl9nYW1tYV9iZiwNCiAgICAgICAgICAgICAgIGRhdGEgPSBzbXIsDQogICAgICAgICAgICAgICBjb3JlcyA9IDQsDQogICAgICAgICAgICAgICBjaGFpbnMgPSA0LA0KICAgICAgICAgICAgICAgcHJpb3IgPSBwcmlvcnNfY3VzdG9tLA0KICAgICAgICAgICAgICAgd2FybXVwID0gMTAwMCwNCiAgICAgICAgICAgICAgIHNlZWQgPSAxNDMwMTksDQogICAgICAgICAgICAgICB0aGluID0gMiwNCiAgICAgICAgICAgICAgIGl0ZXIgPSA4MDAwLA0KICAgICAgICAgICAgICAgc2F2ZV9wYXJzID0gc2F2ZV9wYXJzKGFsbD1UUlVFKSwNCiAgICAgICAgICAgICAgIHNhbXBsZV9wcmlvciA9IFRSVUUsDQogICAgICAgICAgICAgICBmaWxlID0gJ3Ntcl9tb2RfZ2FtbWEnKQ0KcHJpbnQoIk1vZGVsIGNvbXBsZXRlIikNCmBgYA0KDQpBIHByaW9yIG9ubHkgbW9kZWwgICANCg0KYGBge3IsIGVjaG89RkFMU0V9DQojIHNldHdkKG91dHB1dF9tb2RzX3dkKQ0KIyANCiMgc21yX21vZF9nYW1tYV9wcmlvcnMgPC0gYnJtKHNtcl9nYW1tYV9iZiwNCiMgICAgICAgICAgICAgICAgZGF0YSA9IHNtciwNCiMgICAgICAgICAgICAgICAgY29yZXMgPSA0LA0KIyAgICAgICAgICAgICAgICBjaGFpbnMgPSA0LA0KIyAgICAgICAgICAgICAgICBwcmlvciA9IHByaW9yc19jdXN0b20sDQojICAgICAgICAgICAgICAgIHdhcm11cCA9IDEwMDAsDQojICAgICAgICAgICAgICAgIHNlZWQgPSAxNDMwMTksDQojICAgICAgICAgICAgICAgIHRoaW4gPSAyLA0KIyAgICAgICAgICAgICAgICBpdGVyID0gODAwMCwNCiMgICAgICAgICAgICAgICAgc2F2ZV9wYXJzID0gc2F2ZV9wYXJzKGFsbD1UUlVFKSwNCiMgICAgICAgICAgICAgICAgc2FtcGxlX3ByaW9yID0gIm9ubHkiLA0KIyAgICAgICAgICAgICAgICBmaWxlID0gJ3Ntcl9tb2RfZ2FtbWFfcHJpb3JzJykNCiMgcHJpbnQoIk1vZGVsIGNvbXBsZXRlIikNCmBgYA0KDQpIZXJlIHdlIHJlbG9hZCB0aGUgbW9kZWwgICANCg0KYGBge3J9DQpzZXR3ZChvdXRwdXRfbW9kc193ZCkNCg0Kc21yX21vZF9nYW1tYSA8LSAgcmVhZFJEUyhmaWxlID0gInNtcl9tb2RfZ2FtbWEucmRzIikNCiNzbXJfbW9kX2dhbW1hX3ByaW9ycyA8LSAgcmVhZFJEUyhmaWxlID0gInNtcl9tb2RfZ2FtbWFfcHJpb3JzLnJkcyIpDQpgYGANCg0KIyMjIE1vZGVsIGRpYWdub3N0aWNzDQoNCkNoZWNraW5nIG1vZGVsIGNvbnZlcmdlbmNlICAgDQoNCmBgYHtyfQ0KY29sb3Jfc2NoZW1lX3NldCgicmVkIikNCnBsb3Qoc21yX21vZF9nYW1tYSwgYXNrID0gRikNCmBgYA0KDQpMb29raW5nIGF0IHByaW9yIGFuZCBwcmVkaWN0ZWQgZGlzdHJpYnV0aW9ucyBmb3Igb3VyIHBhcmFtZXRlcnMgICANCg0KYGBge3J9DQojIGtlZXBfcGFyYW1ldGVycyA8LSAgYygiYl9JbnRlcmNlcHQiLCAiYl9zaGFwZV9JbnRlcmNlcHQiLCAiYl90ZW1wX21lYW5feiIsICJiX2N5Y2xlc196IiwgImJfbWFzc196IiwgImJfc2FsaW5pdHlfZ3JvdXA5IiwgImJfc2hhcGVfc2FsaW5pdHlfZ3JvdXA5IiwgIkludGVyY2VwdCIsICJJbnRlcmNlcHRfc2hhcGUiKQ0KIyANCiMgcHJpb3Jfc2FtcGxlcyA8LSBhc19kcmF3c19kZihzbXJfbW9kX2dhbW1hX3ByaW9ycykNCiMgcG9zdGVyaW9yX3NhbXBsZXMgPC0gYXNfZHJhd3NfZGYoc21yX21vZF9nYW1tYSkNCiMgDQojIHBvc3Rlcmlvcl9zYW1wbGVzJHNvdXJjZSA8LSAiUG9zdGVyaW9yIg0KIyBwcmlvcl9zYW1wbGVzJHNvdXJjZSA8LSAiUHJpb3IiDQojIA0KIyBjb21iaW5lZF9zYW1wbGVzIDwtICBiaW5kX3Jvd3MocG9zdGVyaW9yX3NhbXBsZXMsIHByaW9yX3NhbXBsZXMpDQojIA0KIyAjIENvbnZlcnQgdG8gbG9uZyBmb3JtYXQgZm9yIGdncGxvdA0KIyBsb25nX3NhbXBsZXMgPC0gcGl2b3RfbG9uZ2VyKGNvbWJpbmVkX3NhbXBsZXMsIA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHMgPSAtc291cmNlLCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lc190byA9ICJwYXJhbWV0ZXIiLCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAidmFsdWUiKSAlPiUgDQojICAgZHBseXI6OmZpbHRlcihwYXJhbWV0ZXIgJWluJSBrZWVwX3BhcmFtZXRlcnMpDQojIA0KIyBnZ3Bsb3QobG9uZ19zYW1wbGVzLCBhZXMoeCA9IHZhbHVlLCBmaWxsID0gc291cmNlKSkgKw0KIyAgIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuNSkgKyAgIyBPdmVybGF5IGRlbnNpdHkgcGxvdHMNCiMgICBmYWNldF93cmFwKH5wYXJhbWV0ZXIsIHNjYWxlcyA9ICJmcmVlIikgKyAgIyBTZXBhcmF0ZSBieSBwYXJhbWV0ZXINCiMgICBsYWJzKHRpdGxlID0gIlByaW9yIHZzIFBvc3RlcmlvciBEaXN0cmlidXRpb25zIiwgeCA9ICJQYXJhbWV0ZXIgVmFsdWUiLCB5ID0gIkRlbnNpdHkiKSArDQojICAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KDQpVc2luZyBsZWF2ZSBvbmUgb3V0IChsb28pIG1lYXN1cmUgb2YgZml0LCB0aGUgbW9kZWwgYXBwZWFycyB0byBwcmVmb3JtIHdlbGwsIG9uZSBQYXJldG8gayBlc3RpbWF0ZXMgZmFsbHMgb3V0c2lkZSB0aGUgZ29vZCByYW5nZSAoMC43LCAxXSAgIA0KDQpgYGB7cn0NCmxvbyhzbXJfbW9kX2dhbW1hKQ0KYGBgDQoNCk1vZGVsIHByZWRpY3Rpb25zIGdlbmVyYWxseSBhbGlnbiB3aXRoIHRoZSBvYnNlcnZlZCBkYXRhLCBidXQgdGhlcmUgaXMgYSBsb3Qgb2YgdW5jZXJ0YWludHkgICAgDQoNCmBgYHtyfQ0KY29sb3Jfc2NoZW1lX3NldCgicmVkIikNCnAgPC0gcHBfY2hlY2soc21yX21vZF9nYW1tYSwgdHlwZSA9ICJkZW5zX292ZXJsYXkiKQ0KcA0KYGBgDQoNCg0KIyMjIFJlc3VsdHMNCg0KV2UgZGlkIG5vdCBzZWUgYSBtZWFuaW5nZnVsIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgcm91dGluZSBtZXRhYm9saWMgcmF0ZSBmb3IgZmlzaCBmcm9tIHRoZSB0d28gc2FsaW5pdHkgdHJlYXRtZW50cy4gDQoNCiMjIyMgVGFibGUgMSANCg0KKipUYWJsZSAxOioqIEZpeGVkIGVmZmVjdCBFc3RpbWF0ZXMgKM6yKSBhbmQgOTUlIENyZWRpYmxlIEludGVydmFscyAoOTUlIENJKSBmcm9tIGEgQmF5ZXNpYW4gR2VuZXJhbGlzZWQgTGluZWFyIE1peGVkIE1vZGVsIChHTE1NKSB3aXRoIGEgR2FtbWEgZGlzdHJpYnV0aW9uIGFuZCBhIGxvZyBsaW5rDQoNCmBgYHtyfQ0KbW9kZWxfZXN0IDwtIGZpeGVmKHNtcl9tb2RfZ2FtbWEsIHByb2JzID0gYygwLjAyNSwgMC45NzUpKSAlPiUgDQogIGFzLmRhdGEuZnJhbWUoKSAlPiUgDQogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJQcmVkaWN0b3IiKSAlPiUgDQogIGRwbHlyOjptdXRhdGUoJ86yJyA9IHJvdW5kKEVzdGltYXRlLCAzKSwNCiAgICAgICAgICAgICAgICBRMi41ID0gcm91bmQoUTIuNSwgMyksDQogICAgICAgICAgICAgICAgUTk3LjUgPSByb3VuZChROTcuNSwgMyksDQogICAgICAgICAgICAgICAgJzk1JSBDSScgPSBwYXN0ZTAoIlsiLCBRMi41LCAiLCAiLCBROTcuNSwgIl0iKSkNCg0KbW9kZWxfZXN0ICU+JSANCiAgZHBseXI6OnNlbGVjdChQcmVkaWN0b3IsICfOsicsICc5NSUgQ0knKSAlPiUgDQogIGd0KCkNCmBgYA0KICANCiAgDQpMb29raW5nIGF0IHRoZSBtYXJnaW5hbCBtZWFuIGRpZmZlcmVuY2UgYmV0d2VlbiBzYWxpbml0eSBncm91cHMgICANCg0KYGBge3J9DQplbV9yZXN1bHRzIDwtIGVtbWVhbnMoc21yX21vZF9nYW1tYSwgfiBzYWxpbml0eV9ncm91cCkgDQpjb250cmFzdF9yZXN1bHRzIDwtIGNvbnRyYXN0KGVtX3Jlc3VsdHMsIG1ldGhvZCA9ICJwYWlyd2lzZSIpDQplbV9yZXN1bHRzX2RmIDwtICBlbV9yZXN1bHRzICU+JSB0aWR5KCkgJT4lIA0KICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCB+IGV4cCguKSkpDQpjb250cmFzdF9yZXN1bHRzX2RmIDwtICBjb250cmFzdF9yZXN1bHRzICU+JSB0aWR5KCkgJT4lIA0KICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCB+IGV4cCguKSkpDQoNCg0KZW1fcmVzdWx0c19kZiAlPiUgDQogIGd0KCkNCmBgYA0KICANClB1bGxpbmcgdGhlIGVtbWVhbnMgZHJhd3MgZm9yIG91ciBwbG90ICAgDQoNCmBgYHtyfQ0KZW1tZWFuc19kcmF3cyA8LSBzbXJfbW9kX2dhbW1hICU+JQ0KICBlbW1lYW5zKH4gc2FsaW5pdHlfZ3JvdXApICU+JQ0KICBnYXRoZXJfZW1tZWFuc19kcmF3cygpICU+JSANCiAgZHBseXI6Om11dGF0ZSgudmFsdWUgPSAgZXhwKC52YWx1ZSksDQogICAgICAgICAgICAgICAgc2FsaW5pdHlfZ3JvdXAgPSBhcy5jaGFyYWN0ZXIoc2FsaW5pdHlfZ3JvdXApKQ0KDQplbW1lYW5zX2NvbnRyYXN0X2RyYXdzIDwtICBzbXJfbW9kX2dhbW1hICU+JQ0KICBlbW1lYW5zKH4gc2FsaW5pdHlfZ3JvdXApICU+JQ0KICBjb250cmFzdChtZXRob2QgPSAicGFpcndpc2UiKSAlPiUNCiAgZ2F0aGVyX2VtbWVhbnNfZHJhd3MoKSAlPiUgDQogIGRwbHlyOjptdXRhdGUoLnZhbHVlID0gIGV4cCgudmFsdWUpKQ0KYGBgDQoNCiMjIyMgRmlndXJlIFNpDQoNClRoaXMgcGxvdCBpcyBpbiB0aGUgbWFpbiB0ZXh0IG9mIHRoZSBtYW51c2NyaXB0IGFzICoqRmlndXJlIDFiKioNCg0KYGBge3J9DQptZWFuX3Ntcl9zYWxpbml0eSA8LSBzbXIgJT4lDQogIGRwbHlyOjpncm91cF9ieShzYWxpbml0eV9ncm91cCkgJT4lDQogIGRwbHlyOjpyZWZyYW1lKG1lYW5fU01SID0gbWVhbihTTVIsIG5hLnJtID0gVFJVRSkpDQoNCg0KZmlnXzFiIDwtIGdncGxvdCgpICsNCiAgZ2VvbV92aW9saW4oZGF0YSA9IHNtciwNCiAgICAgICAgICAgICAgYWVzKHggPSBzYWxpbml0eV9ncm91cCwgeSA9IFNNUiwgZmlsbCA9IHNhbGluaXR5X2dyb3VwKSwNCiAgICAgICAgICAgICAgY29sb3IgPSBOQSwgYWxwaGEgPSAwLjIpICsNCiAgZ2VvbV9qaXR0ZXIoZGF0YSA9IHNtciwNCiAgICAgICAgICAgICAgYWVzKHggPSBzYWxpbml0eV9ncm91cCwgeSA9IFNNUiwgZmlsbCA9IHNhbGluaXR5X2dyb3VwKSwNCiAgICAgICAgICAgICAgc2hhcGUgPSAyMSwgd2lkdGggPSAwLjMsIHNpemUgPSAxLCBjb2xvciA9ICJibGFjayIsIGFscGhhID0gMC4xKSArDQogICAgZ2VvbV9wb2ludChkYXRhID0gbWVhbl9zbXJfc2FsaW5pdHksDQogICAgICAgICAgICAgYWVzKHggPSBzYWxpbml0eV9ncm91cCwgeSA9IG1lYW5fU01SLCBmaWxsID0gc2FsaW5pdHlfZ3JvdXApLA0KICAgICAgICAgICAgIHNpemUgPSA0LCBhbHBoYSA9IDEsIHN0cm9rZSA9IDIsIGNvbG9yID0gImJsYWNrIiwgc2hhcGUgPSAyMSwNCiAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh4ID0gMC4wNSkpICsNCiAgc3RhdF9wb2ludGludGVydmFsKGRhdGEgPSBlbW1lYW5zX2RyYXdzLA0KICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBzYWxpbml0eV9ncm91cCwgeSA9IC52YWx1ZSksDQogICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZ3JleSIsIHBvaW50X2ludGVydmFsID0gIm1lYW5fcWkiLCAud2lkdGggPSAwLjk1LCBzaGFwZSA9IDIxLCAgc3Ryb2tlID0gMiwgcG9pbnRfc2l6ZSA9IDQsIGFscGhhID0gMSwNCiAgICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fbnVkZ2UoeCA9IC0wLjA1KSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjNEI1MzIwIiwgIiMwMDAwODAiKSkgKyAgIyBDdXN0b20gZmlsbCBjb2xvdXJzDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiIzRCNTMyMCIsICIjMDAwMDgwIikpICsNCiAgdGhlbWVfY2xlYW4oKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICBsYWJzKA0KICAgIHN1YnRpdGxlID0gIiIsDQogICAgeCA9ICJTYWxpbml0eSBncm91cCAocHB0KSIsDQogICAgeSA9ICJTdGFuZGFyZCBtZXRhYm9saWMgcmF0ZSAoU01SOyBtZyBPMiBnL2gpIg0KICApDQoNCmZpZ18xYg0KYGBgDQoNCioqRmlndXJlIDFiOioqIFRoZSBzdGFuZGFyZCBtZXRhYm9saWMgcmF0ZSBlc3RpbWF0ZSAobWdeLTFeIE88c3ViPjI8L3N1Yj4gaF4tMV4pIHBsb3R0ZWQgYnkgc2FsaW5pdHkgdHJlYXRtZW50LiBUaGUgc21hbGwgdHJhbnNwYXJlbnQgcG9pbnRzIGFyZSB0aGUgb2JzZXJ2ZWQgdmFsdWVzLCB0aGUgc2hhZGVkIGFyZWEgYmVoaW5kIHRoZSBwb2ludHMgaXMgdGhlIGEga2VybmVsIGRlbnNpdHkgb2YgdGhlIG9ic2VydmVkIGRhdGEsIHRoZSBsYXJnZSBjb2xvdXJlZCBwb2ludCAodG8gdGhlIHJpZ2h0KSBpcyB0aGUgb2JzZXJ2ZWQgbWVhbiwgdGhlIGxhcmdlIGdyZXkgcG9pbnQgd2l0aCBlcnJvciBiYXJzICh0byB0aGUgbGVmdCkgaXMgdGhlIG1vZGVsIGVzdGltYXRlZCBtYXJnaW5hbCBtZWFuIChlZW1lYW4pIDk1JSBDcmVkaWJsZSBJbnRlcnZhbHMgKDk1JSBDSSkuICANCg0KDQojIyBJbmNyZW1lbnRhbCByZWdyZXNzaW9uIGFuYWx5c2VzDQoNCkhlcmUgd2UgYXJlIGZvbGxvd2luZyB0aGUgbWV0aG9kcyBVcmJpbmEgZXQgYWwuICgyMDEyKV5bMV1eIHdpdGggYW4gaW5jcmVtZW50YWwgcmVncmVzc2lvbiBhbmFseXNlcywgaW4gb3JkZXIgdG8gZGV0ZXJtaW5lIHRoZSBiZXN0IGZpdCBmb3IgdGhlIE1PPHN1Yj4yPC9zdWI+IHZzIE88c3ViPjI8L3N1Yj4gZGF0YSAgIA0KDQpUaGlzIGFuYWx5c2lzIGFwcHJvYWNoIGV2YWx1YXRlcyB0aGUgcmVsYXRpdmUgJ2ZpdCcgb2YgZWFjaCBwb2x5bm9taWFsIG9yZGVyIGVxdWF0aW9uIHN0YXJ0aW5nIGF0IHplcm8gYW5kIGluY3JlYXNpbmcgdG8gdGhlIHRoaXJkIG9yZGVyLCBwZXJtaXR0aW5nIGEgbWF0aGVtYXRpY2FsIGFzc2Vzc21lbnQgb2Ygd2hldGhlciBmaXNoIHdlcmUgb3h5Y29uZm9ybWluZyBvciBveHlyZWd1bGF0b3JpbmcuIElmIHRoZSBkYXRhIGlzIGJlc3QgZml0dGVkL3ByZWRpY3RlZCBieSBhIHNpbmdsZSBsaW5lYXIgcmVsYXRpb25zaGlwICgxXnN0Xi1vcmRlciBwb2x5bm9taWFsKSB3aXRoIGEgcG9zaXRpdmUgc2xvcGUsIHRoaXMgd291bGQgc3VnZ2VzdHMgdGhlIGZpc2ggd2VyZSBveHljb25mb3JtaW5nLiBBbHRlcm5hdGVseSwgaWYgdGhlIHJlbGF0aW9uc2hpcCBpcyBiZXN0IG1vZGVsbGVkIGJ5IGEgZmxhdCByZWdyZXNzaW9uICgwXnRoXi1vcmRlciBwb2x5bm9taWFsKSwgb3IgYSBoaWdoZXIgb3JkZXIgcG9seW5vbWlhbCAoMl5uZF4gb3IgM15yZF4tb3JkZXIgcG9seW5vbWlhbCkgdGhlIGZpc2ggaXMgbGlrZWx5IG94eXJlZ3VsYXRvcmluZy4gICANCg0KIyMjIEJ1aWxkaW5nIEJheWVzaWFuIHJlZ3Jlc3Npb25zDQoNCkhlcmUgd2UgYXJlIHVzaW5nIGEgQmF5ZXNpYW4gYXBwcm9hY2ggdG8gbW9kZWwgZml0dGluZyB3aXRoIGJybS4gVGhlc2UgbW9kZWxzIHRha2UgYSBsb25nIHRpbWUgdG8gcnVuLCBzbyBJIGhhdmUgc2F2ZWQgdGhlbSBhbmQgcmUtbG9hZGVkIHRoZW0gdG8gc2F2ZSB0aW1lLiBJIGhhdmUgYWxzbyBzYXZlZCB0aGUgc3VtbWFyeSBkYXRhIHByb2R1Y2VkIGZyb20gdGhlIG1vZGVscywgdG8gc2F2ZSB0aW1lLCB5b3UgY2FuIHNpbXBseSBza2lwIHRoZSBoYXNoZWQgY29kZSBhbmQgaW5wdXQgdGhlIHJlc3VsdGluZyBzdW1tYXJ5IGRhdGEuICAgDQoNCldlIHdpbGwgcnVuIG91ciBjdXN0b20gZnVuY3Rpb24sIGBiYXllc19pbmNyZW1lbnRhbF9yZWdyZXNzaW9uX2J5X2lkYC4gKipUaGlzIGNvZGUgdGFrZXMgYSB3aGlsZSB0byBydW4qKi4gSWYgeW91IGhhdmUgYWxyZWFkeSBydW4gdGhpcyBvbmNlLCBvciBoYXZlIGRvd25sb2FkZWQgdGhlIHNhdmVkIG1vZGVscyBmcm9tIEdpdEh1YiAqKnNraXAgdGhpcyBzdGVwKiogKHRoYXQncyB3aHkgaXRzIGhhc2hlZCBvdXQpLCBhbmQgcnVuIHRoZSBuZXh0IGxpbmUsIHdoaWNoIGxvYWRzIHRoZSBtb2RlbHMuICAgDQoNCmBgYHtyfQ0KIyBvdXRwdXRfbW9kc19iYXllc193ZCA8LSBwYXN0ZTAob3V0cHV0X21vZHNfd2QsICIuL2JheWVzLXJlZ3MiKQ0KIyBpZmVsc2UoIWRpci5leGlzdHMob3V0cHV0X21vZHNfYmF5ZXNfd2QpLCBkaXIuY3JlYXRlKG91dHB1dF9tb2RzX2JheWVzX3dkKSwgIkZvbGRlciBhbHJlYWR5IGV4aXN0cyIpDQojIA0KIyBpZHMgPC0gc2xvcGVfdGlkeSAlPiUNCiMgICBkcGx5cjo6ZGlzdGluY3QoaWQpICU+JQ0KIyAgIHB1bGwoaWQpDQojIA0KIyBwbGFuKG11bHRpc2Vzc2lvbikNCiMgDQojIGZ1dHVyZV9tYXAoDQojICAgaWRzLCANCiMgICBiYXllc19pbmNyZW1lbnRhbF9yZWdyZXNzaW9uX2J5X2lkLA0KIyAgIGlkX25hbWUgPSAiaWQiLA0KIyAgIGRhdGEgPSBzbG9wZV90aWR5LA0KIyAgIHJlc3BvbnNlID0gIk1PMl9nIiwNCiMgICBwcmVkaWN0b3IgPSAiRE8iLA0KIyAgIHNlZWRfbnVtYmVyID0gMTQzMDE5LA0KIyAgIHNhdmVfbW9kZWxzID0gVFJVRSwNCiMgICBtb2Rfb3V0cHV0X3dkID0gb3V0cHV0X21vZHNfYmF5ZXNfd2QNCiMgKQ0KIyANCiMgcGxhbihzZXF1ZW50aWFsKSAgDQpgYGANCg0KTG9hZCBhbGwgbW9kZWxzIGFuZCBzdG9yZSBpbiBhIGxpc3QsIHdpbGwgdXNlIGEgbG90IG9mIG1lbW9yeS4gWW91IGNhbiBhbHNvIHNraXAgdGhpcyBzdGVwIGFuZCBsb2FkIHRoZSByZXN1bHRpbmcgZGF0YSBmcmFtZXMgYmVsb3cuIEkgYW0gdXNpbmcgdGhlIGN1c3RvbSBmdW5jdGlvbiBgbG9hZF9yZHNgLCBzbyB3ZSBjYW4gY29tcGFyZSB0aGVtIGFuZCBnZW5lcmF0ZSBwcmVkaWN0aW9ucy4gICANCg0KYGBge3J9DQojIGJheWVzX3JlZ19tb2RzIDwtIGxvYWRfcmRzKG1vZGVsX2R3ID0gb3V0cHV0X21vZHNfYmF5ZXNfd2QpDQpgYGANCg0KIyMjIE1vZGVsIGZpdHMNCg0KR2V0IG1vZGVsIGZpdCBwYXJhbWV0ZXJzIGxvbyBhbmQgcjIgdXNpbmcgdGhlIGN1c3RvbSBmdW5jdGlvbiwgYGluY3JlbWVudGFsX3JlZ3Jlc3Npb25fYmF5ZXNfZml0c2AuICAgDQoNCmBgYHtyfQ0KIyBzZXR3ZChtb2RfZGF0YV93ZCkNCiMgYmF5ZXNfcmVnX21vZHNfZml0IDwtIGluY3JlbWVudGFsX3JlZ3Jlc3Npb25fYmF5ZXNfZml0cyhtb2RlbHMgPSBiYXllc19yZWdfbW9kcykNCiMgd3JpdGUuY3N2KGJheWVzX3JlZ19tb2RzX2ZpdCwgImJheWVzX3JlZ19tb2RzX2ZpdC5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KICANClJlYWRpbmcgaW4gdGhpcyBtb2RlbCBmaXQgZGF0YSBmcmFtZSwgaW4gdGhlIGNhc2UgeW91IGRpZCBub3QgbG9hZCBpbiBhbGwgdGhlIG1vZGVscy4gICAgDQoNCmBgYHtyfQ0Kc2V0d2QobW9kX2RhdGFfd2QpDQpiYXllc19yZWdfbW9kc19maXQgPC0gcmVhZC5jc3YoImJheWVzX3JlZ19tb2RzX2ZpdC5jc3YiKQ0KYGBgDQoNCioqU2VsZWN0aW5nIHRoZSBiZXN0IGZpdHRpbmcgbW9kZWwqKiAgDQogIA0KZWxwZF9sb28sIG9yIHRoZSBleHBlY3RlZCBsb2cgcG9pbnR3aXNlIHByZWRpY3RpdmUgZGVuc2l0eSBmb3IgbGVhdmUtb25lLW91dCBjcm9zcy12YWxpZGF0aW9uLCBpcyBhIG1ldHJpYyB1c2VkIGluIEJheWVzaWFuIG1vZGVsIGV2YWx1YXRpb24gdG8gYXNzZXNzIHRoZSBwcmVkaWN0aXZlIGFjY3VyYWN5IG9mIGEgbW9kZWwuIFRoZSBlbHBkX2xvbyBpcyBhbiBhcHByb3hpbWF0aW9uIG9mIGhvdyB3ZWxsIHRoZSBtb2RlbCBpcyBleHBlY3RlZCB0byBwcmVkaWN0IG5ldyBkYXRhLCBiYXNlZCBvbiBsZWF2ZS1vbmUtb3V0IGNyb3NzLXZhbGlkYXRpb24uIEhpZ2hlciBlbHBkX2xvbyB2YWx1ZXMgaW5kaWNhdGUgYmV0dGVyIHByZWRpY3RpdmUgcGVyZm9ybWFuY2UuICAgDQoNCmBgYHtyfQ0KYmVzdF9maXRfYmF5ZXNfcmVnIDwtIGJheWVzX3JlZ19tb2RzX2ZpdCAlPiUgDQogIGRwbHlyOjpncm91cF9ieShpZCkgJT4lDQogIGRwbHlyOjptdXRhdGUoZWxwZF9sb29fcmFuayA9IHJhbmsoLWVscGRfbG9vKSkgJT4lIA0KICBkcGx5cjo6c2VsZWN0KGlkLCBtb2RlbF90eXBlLCBlbHBkX2xvbywgcjIsIGVscGRfbG9vX3JhbmssIHIyX3EyLjUsIHIyX3E5Ny41LCBlc3RpbWF0ZV9ETywgY29uZi5sb3dfRE8sIGNvbmYuaGlnaF9ETykNCmBgYA0KDQoNCiMjIyBNb2RlbCBwcmVkaWN0aW9ucw0KDQpQdWxsaW5nIG91ciBtb2RlbCBwcmVkaWN0aW9ucyB1c2luZyBhIGN1c3RvbSBmdW5jdGlvbiBgYmF5ZXNfbW9kX3ByZWRpY3Rpb25zYC4gICANCg0KYGBge3J9DQojc2V0d2QobW9kX2RhdGFfd2QpDQojYmF5ZXNfcmVnX21vZHNfcHJlZGljdGlvbnMgPC0gYmF5ZXNfbW9kX3ByZWRpY3Rpb25zKG1vZGVscyA9IGJheWVzX3JlZ19tb2RzLCBvcmlnaW5hbF9kYXRhID0gc2xvcGVfdGlkeSkNCiN3cml0ZS5jc3YoYmF5ZXNfcmVnX21vZHNfcHJlZGljdGlvbnMsICJiYXllc19yZWdfbW9kc19wcmVkaWN0aW9ucy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KDQpSZWFkaW5nIGluIHRoZSBwcmVkaWN0ZWQgZGF0YSAgIA0KDQpgYGB7cn0NCnNldHdkKG1vZF9kYXRhX3dkKQ0KYmF5ZXNfcmVnX21vZHNfcHJlZGljdGlvbnMgPC0gcmVhZC5jc3YoImJheWVzX3JlZ19tb2RzX3ByZWRpY3Rpb25zLmNzdiIpDQpgYGANCg0KV2UgYXJlIGdvaW5nIHRvIGNvbWJpbmVkIHRoaXMgd2l0aCBvdXIgYmVzdCBmaXR0aW5nIG1vZGVsIGRmLCBzbyB3ZSBrbm93IGhvdyB0aGV5IHJhbmtzIGZvciBMT08uICAgDQoNCmBgYHtyfQ0KYmF5ZXNfcmVnX21vZHNfcHJlZGljdGlvbnMgPC0gZnVsbF9qb2luKGJheWVzX3JlZ19tb2RzX3ByZWRpY3Rpb25zLCBiZXN0X2ZpdF9iYXllc19yZWcsIGJ5ID0gYygiaWQiLCAibW9kZWxfdHlwZSIpKQ0KYGBgDQoNCiMjIyBNb2RlbCBzZWxlY3Rpb24gc3VtbWFyeQ0KDQpUaGUgYmVzdCBmaXR0aW5nIG1vZGVscyB3ZXJlIG1vc3Qgb2Z0ZW4gYSAyXm5kXi1vcmRlciBwb2x5bm9taWFsICgqbiogPSAyMiwgMzglKSBvciBhIDNecmReLW9yZGVyIHBvbHlub21pYWwgKCpuKiA9IDE2LCAyOCUpLiBUaGlzIGNvdWxkIHN1Z2dlc3QgdGhlIHByZXNlbmNlIG9mIGEgY3JpdGljYWwgb3h5Z2VuIHRocmVzaG9sZCAoUGNyaXQpIHdoZXJlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBPPHN1Yj4yPC9zdWI+IGFuZCBNTzxzdWI+Mjwvc3ViPiBjaGFuZ2VzLiBUbyBjb25maXJtIHRoZWlyIGlzIGEgUGNyaXQsIHdlIG5lZWQgdG8gdmFsaWRhdGVkIHRoZSBzaGFwZSBvZiB0aGUgcG9seW5vbWlhbHMgYW5kIGluIHNob3VsZCB1c2UgYSBtb3JlIHNwZWNpZmljIG1vZGVsIHRvIHRlc3QgdGhlIFBjcml0IHZhbHVlLiBJbiBhbnkgY2FzZSwgVGhpcyB0eXBlIG9mIG1vZGVsIGlzIGluZGljYXRpdmUgb2YgKipveHlyZWd1bGF0b3IqKi4gICANCg0KVGhlIG5leHQgbW9zdCBjb21tb24gYXJlIDBedGheLW9yZGVyIGFuZCAxXnN0Xi1vcmRlciBwb2x5bm9taWFscyAoYm90aCAqbiogPSAxMCwgMTclKS4gSW4gdGhlIGNhc2Ugb2YgdGhlIDBedGhePi1vcmRlciBtb2RlbCwgaXQgc3VnZ2VzdHMgdGhhdCBNTzxzdWI+Mjwvc3ViPiBkb2VzIG5vdCBzaG93IGEgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBkZXBlbmRlbmNlIG9uIHRoZSBPPHN1Yj4yPC9zdWI+LiBJbiBvdGhlciB3b3JkcywgdGhlIG1ldGFib2xpYyByYXRlIGRvZXMgbm90IGFkanVzdCBiYXNlZCBvbiBveHlnZW4gYXZhaWxhYmlsaXR5LCBhbmQgdGhlcmUgaXMgbm8gY2xlYXIgY3JpdGljYWwgb3h5Z2VuIHRocmVzaG9sZCAoUGNyaXQpIHdoZXJlIHRoZSByZWxhdGlvbnNoaXAgY2hhbmdlcy4gVGhpcyBpcyBpbmRpY2F0aXZlIG9mIGEgKipveHlyZWd1bGF0b3IqKi4gSW4gdGhlIGNhc2Ugb2YgdGhlIDFec3ReLW9yZGVyIHBvbHlub21pYWxzLCBpdCBzdWdnZXN0IHRoZSBwcmVzZW5jZXMgb2YgbGluZWFyIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIG8yIGFuZCBNTzIsIHdoaWNoIGlzIGluZGljYXRpdmUgb2YgKipveHljb25mb3JtZXIqKi4gSG93ZXZlciwgdG8gYmUgdHJ1ZSBldmlkZW5jZSBvZiBhIG94eWNvbmZvcm1lciB0aGlzIHJlbGF0aW9uc2hpcCBzaG91bGQgYmUgcG9zaXRpdmUgKGkuZS4gYXMgTzxzdWI+Mjwvc3ViPiBmYWxscyBNTzxzdWI+Mjwvc3ViPiBhbHNvIGZhbGxzKS4gT25seSAyIG9mIHRoZSAxMCBpbmRpdmlkdWFscyBiZXN0IG1vZGVsbGVkIHdpdGggYSBsaW5lYXIgZnVuY3Rpb24gaGFkIHBvc2l0aXZlIGVzdGltYXRlcyB3aXRoIGNyZWRpYmxlIGludGVydmFscyB0aGF0IGRpZCBub3Qgb3ZlcmxhcCB3aXRoIHplcm8gKFRhYmxlIFNpKS4gIA0KICANCmBgYHtyfQ0KYmVzdF9tb2QgPC0gYmVzdF9maXRfYmF5ZXNfcmVnICU+JSANCiAgZHBseXI6OmZpbHRlcihlbHBkX2xvb19yYW5rID09IDEpDQoNCnRvdGFsX2Zpc2ggPC0gbnJvdyhiZXN0X21vZCkNCg0KdGFibGVfYndtIDwtIGJlc3RfbW9kICU+JSANCiAgZHBseXI6Omdyb3VwX2J5KG1vZGVsX3R5cGUpICU+JSANCiAgZHBseXI6OnJlZnJhbWUobiA9IGxlbmd0aChpZCksDQogICAgICAgICAgICAgICAgIHBlcmNlbnQgPSByb3VuZCgobi90b3RhbF9maXNoKSoxMDAsMikpICU+JSANCiAgZHBseXI6Om11dGF0ZShiZXN0X21vZGVsX25hbWUgPSBjYXNlX3doZW4oDQogICAgICBtb2RlbF90eXBlID09ICJsbV8wIiB+ICIwdGgtb3JkZXIgcG9seW5vbWlhbCIsDQogICAgICBtb2RlbF90eXBlID09ICJsbV8xIiB+ICIxc3Qtb3JkZXIgcG9seW5vbWlhbCIsDQogICAgICBtb2RlbF90eXBlID09ICJsbV8yIiB+ICIybmQtb3JkZXIgcG9seW5vbWlhbCIsDQogICAgICBtb2RlbF90eXBlID09ICJsbV8zIiB+ICIzcmQtb3JkZXIgcG9seW5vbWlhbCIsDQogICAgICBUUlVFIH4gIkVSUk9SIg0KICAgICkpICU+JSANCiAgZHBseXI6OnNlbGVjdChiZXN0X21vZGVsX25hbWUsIGV2ZXJ5dGhpbmcoKSwgLW1vZGVsX3R5cGUpDQoNCg0KdGFibGVfYndtICU+JSANCiAgIGd0KCkgJT4lIA0KICBjb2xzX2FsaWduKA0KICAgIGFsaWduID0gImNlbnRlciIsIA0KICAgIGNvbHVtbnMgPSBldmVyeXRoaW5nKCkNCiAgKQ0KYGBgDQogIA0KU3VtbWFyeSBvZiBmaXNoIGJlc3QgbW9kZWwgd2l0aCBhIGxpbmVhciBmdW5jdGlvbi4gICANCg0KYGBge3J9DQp0YWJsZV9sbV8xIDwtIGJlc3RfbW9kICU+JSANCiAgZHBseXI6OmZpbHRlcihtb2RlbF90eXBlID09ICJsbV8xIikgJT4lIA0KICBkcGx5cjo6bXV0YXRlKHJfc3FfY2kgPSBwYXN0ZTAocm91bmQocjIsIDMpLCAiICgiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvdW5kKHIyX3EyLjUsIDMpLCAi4oCTIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChyMl9xOTcuNSwgMyksICIpIiksDQogICAgICAgICAgICAgICAgZXN0X2NpID0gcGFzdGUwKHJvdW5kKGVzdGltYXRlX0RPLCA2KSwgIiAoIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvdW5kKGNvbmYubG93X0RPLCA2KSwgIuKAkyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChjb25mLmhpZ2hfRE8sIDYpLCAiKSIpLA0KICAgICAgICAgICAgICAgIGNvbmZvcm1lciA9IGlmX2Vsc2UoY29uZi5sb3dfRE8gPiAwLCAiQ29uZm9ybWluZyIsICJOb3QgY29uZm9ybWluZyIpKSAlPiUgDQogIGRwbHlyOjpzZWxlY3QoaWQsIHJfc3FfY2ksIGVzdF9jaSwgY29uZm9ybWVyKSAlPiUgDQogIGRwbHlyOjphcnJhbmdlKGNvbmZvcm1lcikgJT4lIA0KICBkcGx5cjo6dW5ncm91cCgpDQoNCg0KdGFibGVfbG1fMSAlPiUgDQogIGd0KCkgJT4lIA0KICBjb2xzX2FsaWduKA0KICAgIGFsaWduID0gImNlbnRlciIsIA0KICAgIGNvbHVtbnMgPSBldmVyeXRoaW5nKCkNCiAgKSAlPiUgDQogIGNvbHNfbGFiZWwoDQogICAgaWQgPSAiRmlzaCBJRCIsDQogICAgcl9zcV9jaSA9ICJyMiAoQ0kpIiwNCiAgICBlc3RfY2kgPSAiRXN0aW1hdGUgKENJKSIsDQogICAgY29uZm9ybWVyID0gIkV2aWRlbmNlIG9mIG94eWNvbmZvcm1pbmciDQogICkNCmBgYA0KDQojIyMjIFBsb3RpbmcgbW9kZWxzDQogIA0KTm93IHdlIGFyZSBwbG90dGluZyBlYWNoIG9mIHRoZSByZWdyZXNzaW9ucy4gRmlyc3QgbWFraW5nIGEgZGlyZWN0b3J5IHRvIHNhdmUgdGhlIGZpZ3VyZXMgICANCg0KYGBge3IsIGVjaG89RkFMU0V9DQppbmNyZW1lbnRhbF9yZWdfYmF5ZXNfd2QgPC0gZmlsZS5wYXRoKG91dHB1dF9maWdfd2QsICJpbmNyZW1lbnRhbF9yZWdyZXNzaW9ucy4vYmF5ZXMiKQ0KaWYgKCFkaXIuZXhpc3RzKGluY3JlbWVudGFsX3JlZ19iYXllc193ZCkpIHsNCiAgZGlyLmNyZWF0ZShpbmNyZW1lbnRhbF9yZWdfYmF5ZXNfd2QpDQp9DQpgYGANCiAgDQpQbG90aW5nIGFsbCByZWdyZXNzaW9uLCBhbmQgaGlnaGxpZ2h0aW5nIHRoZSBtb2RlbCB0aGF0IGhhcyB0aGUgYmVzdCBmaXQsIGJhc2VkIG9uIEFJQyB2YWx1ZXMgICANCg0KYGBge3IsIGZpZy5oZWlnaHQgPSAzLCBmaWcud2lkdGg9IDR9DQojIENyZWF0ZSBhIGxpc3QgdG8gc3RvcmUgdGhlIHBsb3RzDQpwbG90cyA8LSBsaXN0KCkNCm1vZGVsX3ByZWRzX2xpc3QgPC0gbGlzdCgpDQoNCmZvciAoaWRfaSBpbiBpZHMpIHsNCiAgDQogICMgRmlsdGVyIGRhdGEgZm9yIHRoZSBjdXJyZW50IElEDQogIGRmX2kgPC0gYmF5ZXNfcmVnX21vZHNfcHJlZGljdGlvbnMgJT4lDQogICAgZHBseXI6OmZpbHRlcihpZCA9PSBpZF9pKSAlPiUgDQogICAgZHBseXI6Om11dGF0ZShsaW5lX3NpemUgPSBpZl9lbHNlKGVscGRfbG9vX3JhbmsgPT0gMSwgMiwgMSksDQogICAgICAgICAgIGFscGhhX3ZhbHVlID0gaWZfZWxzZShlbHBkX2xvb19yYW5rID09IDEsIDEsIDAuNCkpDQogIA0KICB4X21pbiA8LSBkZl9pICU+JQ0KICAgIGRwbHlyOjpyZWZyYW1lKG1pbiA9IG1pbihETyksIG5hLnJtID0gVFJVRSkgJT4lIA0KICAgIGRwbHlyOjpwdWxsKG1pbikNCiAgDQogIHlfbWF4IDwtIGRmX2kgJT4lDQogICAgZHBseXI6OnJlZnJhbWUobWF4ID0gbWF4KE1PMl9nKSwgbmEucm0gPSBUUlVFKSAlPiUgDQogICAgZHBseXI6OnB1bGwobWF4KQ0KICANCiAgYmVzdF93ZWlnaHRlZF9tb2RlbF9pIDwtIGJlc3RfZml0X2JheWVzX3JlZyAlPiUgDQogICAgZHBseXI6OmZpbHRlcihpZCA9PSBpZF9pICYgZWxwZF9sb29fcmFuayA9PSAxKQ0KICANCiAgcG9seV9pX25hbWUgPC0gYmVzdF93ZWlnaHRlZF9tb2RlbF9pICU+JQ0KICAgIGRwbHlyOjptdXRhdGUobmFtZSA9IGNhc2Vfd2hlbigNCiAgICAgIG1vZGVsX3R5cGUgPT0gImxtXzAiIH4gIjB0aC1vcmRlciIsDQogICAgICBtb2RlbF90eXBlID09ICJsbV8xIiB+ICIxc3Qtb3JkZXIiLA0KICAgICAgbW9kZWxfdHlwZSA9PSAibG1fMiIgfiAiMm5kLW9yZGVyIiwNCiAgICAgIG1vZGVsX3R5cGUgPT0gImxtXzMiIH4gIjNyZC1vcmRlciIsDQogICAgICBUUlVFIH4gIkVSUk9SIg0KICAgICkpICU+JSANCiAgICBkcGx5cjo6cHVsbChuYW1lKQ0KICANCiAgcl9pIDwtIGJlc3Rfd2VpZ2h0ZWRfbW9kZWxfaSAlPiUgDQogICAgZHBseXI6Om11dGF0ZShyX3NxX2NpID0gcGFzdGUwKHJvdW5kKHIyLCAzKSwgIiAoIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChyMl9xMi41LCAzKSwgIuKAkyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm91bmQocjJfcTk3LjUsIDMpLCAiKSIpKSAlPiUgDQogICAgZHBseXI6OnB1bGwocl9zcV9jaSkNCg0KICAjIENyZWF0ZSB0aGUgcGxvdA0KICBwbG90X2kgPC0gZ2dwbG90KCkgKw0KICAgIGdlb21fcmliYm9uKGRhdGEgPSBkZl9pLA0KICAgICAgICAgICAgICAgIGFlcyh4ID0gRE8sIHkgPSBwcmVkaWN0ZWQsIHltaW4gPSBwcmVkX2xvd2VyLCB5bWF4ID0gcHJlZF91cHBlciwgZmlsbCA9IG1vZGVsX3R5cGUpLA0KICAgICAgICAgICAgICAgIGFscGhhID0gMC4xKSArDQogICAgZ2VvbV9saW5lKGRhdGEgPSBkZl9pLCANCiAgICAgICAgICAgICAgYWVzKHggPSBETywgeSA9IHByZWRpY3RlZCwgY29sb3VyID0gbW9kZWxfdHlwZSwgc2l6ZSA9IGxpbmVfc2l6ZSwgYWxwaGEgPSBhbHBoYV92YWx1ZSkpICsNCiAgICBnZW9tX3BvaW50KGRhdGEgPSBkZl9pICU+JSBkcGx5cjo6ZmlsdGVyKGVscGRfbG9vX3JhbmsgPT0gMSksIGFlcyh4ID0gRE8sIHkgPSBNTzJfZyksIGFscGhhID0gMC42LCBjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMikgKw0KICAgIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygicmVkIiwgImJsdWUiLCAiZ3JlZW4iLCAicHVycGxlIiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiMHRoIE9yZGVyIiwgIjFzdCBPcmRlciIsICIybmQgT3JkZXIiLCAiM3JkIE9yZGVyIikpICsNCiAgICBzY2FsZV9zaXplX2lkZW50aXR5KCkgKyAgIyBVc2UgdGhlIHNpemUgdmFsdWVzIGRpcmVjdGx5DQogICAgc2NhbGVfYWxwaGFfaWRlbnRpdHkoZ3VpZGUgPSAibm9uZSIpICsgICMgUmVtb3ZlIHRoZSBhbHBoYSBsZWdlbmQgDQogICAgYW5ub3RhdGUoInRleHQiLCB4ID0geF9taW4sIA0KICAgICAgICAgICAgIHkgPSB5X21heCwgDQogICAgICAgICAgICAgbGFiZWwgPSBwYXN0ZTAoIkJlc3QgZml0OiAiLHBvbHlfaV9uYW1lLCAiXG4iLCAicjIgPSAiLCByX2kpLCANCiAgICAgICAgICAgICBoanVzdCA9IDAsIHZqdXN0ID0gMSwgc2l6ZSA9IDQpICsNCiAgICBsYWJzKA0KICAgICAgdGl0bGUgPSBwYXN0ZShpZF9pKSwNCiAgICAgIHggPSAiRGlzc29sdmVkIG94eWdlbiBwZXJjZW50YWdlIChETykiLA0KICAgICAgeSA9ICJNTzIgKG8yIG1nL2cvaCkiLA0KICAgICAgY29sb3VyID0gIk1vZGVsIikgKw0KICAgIHRoZW1lX2NsYXNzaWMoKSArDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KICANCiAgIyBTdG9yZSB0aGUgcGxvdA0KICBwbG90c1tbaWRfaV1dIDwtIHBsb3RfaQ0KICANCiAgcHJpbnQocGxvdF9pKQ0KfQ0KDQoNCiNUbyBzYXZlIGFsbCBwbG90cyB0byBpbmRpdmlkdWFsIGZpbGVzDQpmb3IgKGlkX2kgaW4gaWRzKSB7DQogIGdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMChpbmNyZW1lbnRhbF9yZWdfYmF5ZXNfd2QsICIuL3Bsb3RfIiwgaWRfaSwgIi5wbmciKSwgcGxvdCA9IHBsb3RzW1tpZF9pXV0sIHdpZHRoID0gOCwgaGVpZ2h0ID0gNikNCn0NCmBgYA0KDQojIyFOb3RlOiBuZWVkIG9ubHkgYmVzdCBmaXR0aW5nIG1vZGVsIQ0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCm91dHB1dF9tb2RzX2JheWVzX2dsb2JhbF93ZCA8LSBwYXN0ZTAob3V0cHV0X21vZHNfd2QsICIuL2JheWVzLXJlZ3MtZ2xvYmFsIikNCmlmZWxzZSghZGlyLmV4aXN0cyhvdXRwdXRfbW9kc19iYXllc19nbG9iYWxfd2QpLCBkaXIuY3JlYXRlKG91dHB1dF9tb2RzX2JheWVzX2dsb2JhbF93ZCksICJGb2xkZXIgYWxyZWFkeSBleGlzdHMiKQ0KYGBgDQoNCkhlcmUgd2UgYXJlIGdyb3VwaW5nIGZpc2ggYnkgYmVzdCBmaXR0aW5nIG1vZGVsIGFuZCBnZXR0aW5nIGFuIGF2ZXJhZ2UgdHJlbmQuIEkgaGF2ZSBub3QgZmluaXNoZWQgdGhpcyBjb2RlIHlldA0KDQpgYGB7cn0NCiMgDQojIGJlc3RfZml0IDwtIGJheWVzX3JlZ19tb2RzX3ByZWRpY3Rpb25zICU+JSANCiMgICBkcGx5cjo6ZmlsdGVyKGVscGRfbG9vX3JhbmsgPT0gMSkNCiMgDQojIGlkcyA8LSBiZXN0X2ZpdCAlPiUNCiMgICBkcGx5cjo6ZGlzdGluY3QobW9kZWxfdHlwZSkgJT4lDQojICAgcHVsbChtb2RlbF90eXBlKQ0KIyANCiMgcGxhbihtdWx0aXNlc3Npb24pDQojIA0KIyBmdXR1cmVfbWFwKA0KIyAgIGlkcywNCiMgICBiYXllc19pbmNyZW1lbnRhbF9yZWdyZXNzaW9uX2J5X2lkLA0KIyAgIGlkX25hbWUgPSAibW9kZWxfdHlwZSIsDQojICAgZGF0YSA9IGJlc3RfZml0LA0KIyAgIHJlc3BvbnNlID0gIk1PMl9nIiwNCiMgICBwcmVkaWN0b3IgPSAiRE8iLA0KIyAgIHNhdmVfbW9kZWxzID0gVFJVRSwNCiMgICBtb2Rfb3V0cHV0X3dkID0gb3V0cHV0X21vZHNfYmF5ZXNfZ2xvYmFsX3dkDQojICkNCiMgDQojIHBsYW4oc2VxdWVudGlhbCkNCmBgYA0KDQoNCmBgYHtyfQ0KI2JheWVzX3JlZ19tb2RzIDwtIGxvYWRfcmRzKG1vZGVsX2R3ID0gb3V0cHV0X21vZHNfYmF5ZXNfZ2xvYmFsX3dkKQ0KYGBgDQoNCg0KYGBge3J9DQojIGdncGxvdCgpICsNCiMgICBnZW9tX3JpYmJvbihkYXRhID0gZGZfaSwNCiMgICAgICAgICAgICAgICBhZXMoeCA9IERPLCB5ID0gcHJlZGljdGVkLCB5bWluID0gcHJlZF9sb3dlciwgeW1heCA9IHByZWRfdXBwZXIsIGZpbGwgPSBtb2RlbF90eXBlKSwgYWxwaGEgPSAwLjEpICsNCiMgICBnZW9tX2xpbmUoZGF0YSA9IGRmX2ksDQojICAgICAgICAgICAgIGFlcyh4ID0gRE8sIHkgPSBwcmVkaWN0ZWQsIGNvbG91ciA9IG1vZGVsX3R5cGUsIHNpemUgPSBsaW5lX3NpemUsIGFscGhhID0gYWxwaGFfdmFsdWUpKSArDQojICAgZ2VvbV9wb2ludChkYXRhID0gYmF5ZXNfcmVnX21vZHNfcHJlZGljdGlvbnMgJT4lIGRwbHlyOjpmaWx0ZXIoZWxwZF9sb29fcmFuayA9PSAxKSwNCiMgICAgICAgICAgICAgIGFlcyh4ID0gRE8sIHkgPSBNTzJfZyksIGFscGhhID0gMC4xLCBjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMSkgKw0KIyAgIGdlb21fbGluZShkYXRhID0gYmF5ZXNfcmVnX21vZHNfcHJlZGljdGlvbnMgJT4lIGRwbHlyOjpmaWx0ZXIoZWxwZF9sb29fcmFuayA9PSAxKSwNCiMgICAgICAgICAgICAgYWVzKHggPSBETywgeSA9IHByZWRpY3RlZCwgYnkgPSBpZCksIGFscGhhID0gMC4yKSArDQojICAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKCJyZWQiLCAiYmx1ZSIsICJncmVlbiIsICJwdXJwbGUiKSwNCiMgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIjB0aCBPcmRlciIsICIxc3QgT3JkZXIiLCAiMm5kIE9yZGVyIiwgIjNyZCBPcmRlciIpKSArDQojICAgYW5ub3RhdGUoInRleHQiLCB4ID0geF9taW4sDQojICAgICAgICAgICAgeSA9IHlfbWF4LA0KIyAgICAgICAgICAgIGxhYmVsID0gcGFzdGUwKCJCZXN0IGZpdDogIixwb2x5X2lfbmFtZSwgIlxuIiwgInIyID0gIiwgcl9pKSwNCiMgICAgICAgICAgICBoanVzdCA9IDAsIHZqdXN0ID0gMSwgc2l6ZSA9IDQpICsNCiMgICBmYWNldF93cmFwKH5tb2RlbF90eXBlKSArDQojICAgbGFicygNCiMgICAgIHRpdGxlID0gcGFzdGUoIk1vZGVsIEZpdHMgdnMgUmF3IERhdGEgZm9yIElEIiwgaWRfaSksDQojICAgICB4ID0gIkRpc3NvbHZlZCBveHlnZW4gcGVyY2VudGFnZSAoRE8pIiwNCiMgICAgIHkgPSAiTU8yIChtZyBvMi9nL2gpIiwNCiMgICAgIGNvbG91ciA9ICJNb2RlbCIpICsNCiMgICB0aGVtZV9jbGFzc2ljKCkNCmBgYA0KDQpgYGB7cn0NCiMgZ2xvYmFsX21vZGVscyA8LSBsaXN0KA0KIyAgICAgbG1fMCA9IGxtZXIoTU8yX2cgfiAxICsgKDF8aWQpLCBkYXRhID0gc2xvcGVfdGlkeSAlPiUgDQojICAgICAgICAgICAgICAgICAgIGRwbHlyOjpmaWx0ZXIocG9seSA9PSAwKSwgd2VpZ2h0cyA9IHdlaWdodF9zbXIpLA0KIyAgICAgbG1fMSA9IGxtZXIoTU8yX2cgfiBETyArICgxfGlkKSwgZGF0YSA9IHNsb3BlX3RpZHkgJT4lIA0KIyAgICAgICAgICAgICAgICAgICBkcGx5cjo6ZmlsdGVyKHBvbHkgPT0gMSksIHdlaWdodHMgPSB3ZWlnaHRfc21yKSwNCiMgICAgIGxtXzIgPSBsbWVyKE1PMl9nIH4gcG9seShETywgMikgKyAoMXxpZCksIGRhdGEgPSBzbG9wZV90aWR5ICU+JSANCiMgICAgICAgICAgICAgICAgICAgZHBseXI6OmZpbHRlcihwb2x5ID09IDIpLCB3ZWlnaHRzID0gd2VpZ2h0X3NtciksDQojICAgICBsbV8zID0gbG1lcihNTzJfZyB+IHBvbHkoRE8sIDMpICsgKDF8aWQpLCBkYXRhID0gc2xvcGVfdGlkeSAlPiUgDQojICAgICAgICAgICAgICAgICAgIGRwbHlyOjpmaWx0ZXIocG9seSA9PSAzKSwgd2VpZ2h0cyA9IHdlaWdodF9zbXIpDQojICAgKQ0KIyANCiMgDQojIGdsb2JhbF9wcmVkaWN0aW9ucyA8LSBkYXRhLmZyYW1lKERPID0gc2VxKG1pbihzbG9wZV90aWR5JERPKSwgbWF4KHNsb3BlX3RpZHkkRE8pLCBsZW5ndGgub3V0ID0gMTAwKSkNCiMgDQojIGZvciAobW9kZWxfbmFtZSBpbiBuYW1lcyhnbG9iYWxfbW9kZWxzKSkgew0KIyAgIHByZWRpY3Rpb25zIDwtIHByZWRpY3QoDQojICAgICBnbG9iYWxfbW9kZWxzW1ttb2RlbF9uYW1lXV0sIA0KIyAgICAgbmV3ZGF0YSA9IGdsb2JhbF9wcmVkaWN0aW9ucywgDQojICAgICByZS5mb3JtID0gTkEsICAjIEV4Y2x1ZGVzIHJhbmRvbSBlZmZlY3RzIChwb3B1bGF0aW9uLWxldmVsIHByZWRpY3Rpb25zKQ0KIyAgICAgc2UuZml0ID0gVFJVRSAgIyBSZXR1cm5zIHN0YW5kYXJkIGVycm9ycw0KIyAgICkNCiMgICANCiMgICBnbG9iYWxfcHJlZGljdGlvbnNbW3Bhc3RlMChtb2RlbF9uYW1lLCAiX2ZpdCIpXV0gPC0gcHJlZGljdGlvbnMkZml0DQojICAgZ2xvYmFsX3ByZWRpY3Rpb25zW1twYXN0ZTAobW9kZWxfbmFtZSwgIl9sd3IiKV1dIDwtIHByZWRpY3Rpb25zJGZpdCAtIDEuOTYgKiBwcmVkaWN0aW9ucyRzZS5maXQNCiMgICBnbG9iYWxfcHJlZGljdGlvbnNbW3Bhc3RlMChtb2RlbF9uYW1lLCAiX3VwciIpXV0gPC0gcHJlZGljdGlvbnMkZml0ICsgMS45NiAqIHByZWRpY3Rpb25zJHNlLmZpdA0KIyB9DQojIA0KIyBnbG9iYWxfcHJlZGljdGlvbnNfbG9uZyA8LSBnbG9iYWxfcHJlZGljdGlvbnMgJT4lDQojICAgcGl2b3RfbG9uZ2VyKA0KIyAgICAgY29scyA9IG1hdGNoZXMoImxtXy4qX2ZpdHxsbV8uKl9sd3J8bG1fLipfdXByIiksDQojICAgICBuYW1lc190byA9IGMoIm1vZGVsIiwgIi52YWx1ZSIpLA0KIyAgICAgbmFtZXNfcGF0dGVybiA9ICIobG1fXFxkKylfKC4qKSINCiMgICApICU+JQ0KIyAgIGRwbHlyOjptdXRhdGUoYmVzdF9tb2RlbF9uYW1lID0gY2FzZV93aGVuKA0KIyAgICAgbW9kZWwgPT0gImxtXzAiIH4gIjB0aC1vcmRlciBwb2x5bm9taWFsIiwNCiMgICAgIG1vZGVsID09ICJsbV8xIiB+ICIxc3Qtb3JkZXIgcG9seW5vbWlhbCIsDQojICAgICBtb2RlbCA9PSAibG1fMiIgfiAiMm5kLW9yZGVyIHBvbHlub21pYWwiLA0KIyAgICAgbW9kZWwgPT0gImxtXzMiIH4gIjNyZC1vcmRlciBwb2x5bm9taWFsIiwNCiMgICAgIFRSVUUgfiAiRVJST1IiDQojICAgKSkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBiZXN0X3dlaWdodGVkX21vZGVsX3ByZWQgPC0gYmVzdF93ZWlnaHRlZF9tb2RlbCAlPiUgDQojICAgZHBseXI6OmxlZnRfam9pbiguLCBtb2RlbF9wcmVkc19kZiwgYnkgPSBjKCJpZCIsICJtb2RlbCIpKSAlPiUgDQojICAgZHBseXI6OnVuZ3JvdXAoKSAlPiUgDQojICAgZHBseXI6Om11dGF0ZShiZXN0X21vZGVsX25hbWUgPSBjYXNlX3doZW4oDQojICAgICAgIHBvbHkgPT0gMCB+ICIwdGgtb3JkZXIgcG9seW5vbWlhbCIsDQojICAgICAgIHBvbHkgPT0gMSB+ICIxc3Qtb3JkZXIgcG9seW5vbWlhbCIsDQojICAgICAgIHBvbHkgPT0gMiB+ICIybmQtb3JkZXIgcG9seW5vbWlhbCIsDQojICAgICAgIHBvbHkgPT0gMyB+ICIzcmQtb3JkZXIgcG9seW5vbWlhbCIsDQojICAgICAgIFRSVUUgfiAiRVJST1IiDQojICAgICApKQ0KIyANCiMgYW5ub3RhdGlvbl9kYXRhIDwtIHRhYmxlX2J3bSAlPiUNCiMgICBkcGx5cjo6c2VsZWN0KGJlc3RfbW9kZWxfbmFtZSwgbikgDQojIA0KIyBmaWdfMSA8LSBnZ3Bsb3QoKSArDQojICAgZ2VvbV9saW5lKGRhdGEgPSBiZXN0X3dlaWdodGVkX21vZGVsX3ByZWQsIA0KIyAgICAgICAgICAgICBhZXMoeCA9IERPLCB5ID0gTU8yX3ByZWQsIGNvbG9yID0gaWQpLCBzaXplID0gMSwgYWxwaGEgPSAxKSArDQojICAgZ2VvbV9wb2ludChkYXRhID0gc2xvcGVfdGlkeSwgYWVzKHggPSBETywgeSA9IE1PMl9nKSwgYWxwaGEgPSAwLjEsIGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAyKSArDQojICAgZ2VvbV9yaWJib24oZGF0YSA9IGdsb2JhbF9wcmVkaWN0aW9uc19sb25nLCANCiMgICAgICAgICAgICAgICBhZXMoeCA9IERPLCB5bWluID0gbHdyLCB5bWF4ID0gdXByLCBncm91cCA9IG1vZGVsKSwgDQojICAgICAgICAgICAgICAgZmlsbCA9ICIjRkM2Qzg1IiwgYWxwaGEgPSAwLjIpICsgICMgU2hhZGVkIGNvbmZpZGVuY2UgaW50ZXJ2YWxzDQojICAgZ2VvbV9saW5lKGRhdGEgPSBnbG9iYWxfcHJlZGljdGlvbnNfbG9uZywgDQojICAgICAgICAgICAgIGFlcyh4ID0gRE8sIHkgPSBmaXQpLCBzaXplID0gMS41LCBjb2xvciA9ICIjRkYwMDdGIikgKw0KIyAgIGZhY2V0X3dyYXAofmJlc3RfbW9kZWxfbmFtZSkgKw0KIyAgIHNjYWxlX2NvbG9yX2dyZXkoc3RhcnQgPSAwLjEsIGVuZCA9IDAuOSkgKw0KIyAgIGxhYnMoDQojICAgICAgIHRpdGxlID0gcGFzdGUoIk1vZGVsIGVzdGltYXRlcyBhbmQgb2JzZXJ2ZWQgZGF0YSBncm91cGVkIGJ5IGJlc3QgZml0dGluZyBtb2RlbCIpLA0KIyAgICAgICB4ID0gIkRpc3NvbHZlZCBveHlnZW4gcGVyY2VudGFnZSAoRE8pIiwNCiMgICAgICAgeSA9ICJNTzIgKE8yIG1nL2cvaCkiKSArDQojICAgdGhlbWVfY2xhc3NpYygpICsNCiMgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiMgICBnZW9tX3RleHQoZGF0YSA9IGFubm90YXRpb25fZGF0YSwgDQojICAgICAgICAgICAgIGFlcyh4ID0gLUluZiwgeSA9IEluZiwgbGFiZWwgPSBwYXN0ZTAoIml0YWxpYyhuKSA9PSAiLCBuKSksIA0KIyAgICAgICAgICAgICBoanVzdCA9IC0wLjEsIHZqdXN0ID0gMS4yLCBpbmhlcml0LmFlcyA9IEZBTFNFLCBwYXJzZSA9IFRSVUUpDQojIGZpZ18xDQpgYGANCg0KIyMgUGNyaXQgQ2hhYm90IG1ldGhvZA0KDQpGb3IgdGhvc2UgZmlzaCB0aGF0IHdlcmUgYmVzdCBtb2RlbGxlZCB3aXRoIGEgMl5uZF4gb3IgM15yZF4tb3JkZXIgcG9seW5vbWlhbCAoKm4qID0gNDYpIHdlIHdpbGwgY2hlY2sgdG8gc2VlIGlmIGEgUGNyaXQgaXMgcHJlc2VudC4gV2UgYXJlIGZpbHRlcmluZyB0aGUgZGF0YSBmb3Igb25seSB0aG9zZSBmaXNoLiAgIA0KDQpgYGB7cn0NCmhpZ2hlcl9vcmRlciA8LSBjKCJsbV8yIiwgImxtXzMiKQ0KDQpjaGVja19wY3JpdF9pZHMgPC0gYmVzdF9tb2QgJT4lIA0KICBkcGx5cjo6ZmlsdGVyKG1vZGVsX3R5cGUgJWluJSBoaWdoZXJfb3JkZXIpICU+JSANCiAgZHBseXI6OmRpc3RpbmN0KGlkKSAlPiUgDQogIHB1bGwoaWQpDQoNCmNoZWNrX3Bjcml0X2RmIDwtIHNsb3BlX3RpZHkgJT4lIA0KICBkcGx5cjo6ZmlsdGVyKGlkICVpbiUgY2hlY2tfcGNyaXRfaWRzKQ0KYGBgDQogIA0KV2Ugd2lsbCBjYWxjdWxhdGUgUGNyaXQgdXNpbmcgQ2hhYm90IG1ldGhvZCBhbmQgZnVuY3Rpb24gYGNhbGNPMmNyaXRgLiBXZSBhcmUgdXNpbmcgb3VyIGVzdGltYXRlcyBmb3IgU01SIChtZWFuIG9mIGxvd2VzdCB0aHJlZSkuICAgDQoNClRoaXMgZnVuY3Rpb24gdXNlcyB0aGUgZmlmdGggcGVyY2VudGlsZSBvZiB0aGUgTU88c3ViPjI8L3N1Yj4gdmFsdWVzIG9ic2VydmVkIGF0IGRpc3NvbHZlZCBveHlnZW4gbGV2ZWxzIOKJpSA4MCUgYWlyIHNhdHVyYXRpb24gYXMgdGhlIGNyaXRlcmlvbiB0byBhc3Nlc3MgbG93ICBNTzxzdWI+Mjwvc3ViPiB2YWx1ZXMuIFRoZSBhbGdvcml0aG0gdGhlbiBpZGVudGlmaWVzIGFsbCB0aGUgTU88c3ViPjI8L3N1Yj4gbWVhc3VyZW1lbnRzIGdyZWF0ZXIgdGhhbiB0aGlzIG1pbmltYWxseSBhY2NlcHRhYmxlIE1PPHN1Yj4yPC9zdWI+IHZhbHVlLiBXaXRoaW4gdGhpcyBzdWItc2V0LCBpdCBpZGVudGlmaWVzIHRoZSDMhyBNTzIgbWVhc3VyZW1lbnQgbWFkZSBhdCB0aGUgbG93ZXN0IERPIGFuZCB0aGVyZWFmdGVyIGNvbnNpZGVycyB0aGlzIERPIGFzIGNhbmRpZGF0ZSBmb3IgYnJlYWtwb2ludCAobmFtZWQgcGl2b3RETyBpbiB0aGUgc2NyaXB0KS4gQSByZWdyZXNzaW9uIGlzIHRoZW4gY2FsY3VsYXRlZCB1c2luZyBvYnNlcnZhdGlvbnMgYXQgRE8gbGV2ZWxzIDwgcGl2b3RETywgYW5kIGEgZmlyc3QgZXN0aW1hdGUgb2YgTzJjcml0IGlzIGNhbGN1bGF0ZWQgYXMgdGhlIGludGVyc2VjdGlvbiBvZiB0aGlzIHJlZ3Jlc3Npb24gbGluZSB3aXRoIHRoZSBob3Jpem9udGFsIGxpbmUgcmVwcmVzZW50aW5nIFNNUi4gVGhlIHNjcmlwdCB0aGVuIGdvZXMgdGhyb3VnaCB2YWxpZGF0aW9uIHN0ZXBzIHRvIGVuc3VyZSB0aGF0IHRoZSBzbG9wZSBvZiB0aGUgcmVncmVzc2lvbiBpcyBub3Qgc28gbG93IHRoYXQgdGhlIGxpbmUsIHByb2plY3RlZCB0byBub3Jtb3hpYyBETyBsZXZlbHMsIHBhc3NlcyBiZWxvdyBhbnkgTU8yIHZhbHVlcyBvYnNlcnZlZCBpbiBub3Jtb3hpYS4gSXQgYWxzbyBlbnN1cmVzIHRoYXQgdGhlIGludGVyY2VwdCBpcyBub3QgZ3JlYXRlciB0aGFuIHplcm8uIENvcnJlY3RpdmUgbWVhc3VyZXMgYXJlIHRha2VuIGlmIHN1Y2ggcHJvYmxlbXMgYXJlIGVuY291bnRlcmVkLiAgDQoNCmxvd2VzdE1PMiBkZWZhdWx0IGlzIHRoZSBgcXVhbnRpbGUoTU8yW0RPID49IDgwXSwgcD0wLjA1KWAuIEl0IGlzIHVzZWQgdG8gc2VnbWVudCB0aGUgZGF0YSBhbmQgbG9jYXRlIHRoZSBwaXZvdERPLiAgIA0KDQpgYGB7cn0NCmlkcyA8LSBjaGVja19wY3JpdF9kZiAlPiUgDQogIGRwbHlyOjpkaXN0aW5jdChpZCkgJT4lIA0KICBkcGx5cjo6cHVsbCgpDQoNCnBjcml0X21vZGVsX2RmX2xpc3QgPC0gbGlzdCgpDQpwY3JpdF9tb2RlbHMgPC0gIGxpc3QoKQ0KDQpmb3IgKGlkX2kgaW4gaWRzKSB7DQoNCmRmX2kgPC0gY2hlY2tfcGNyaXRfZGYgJT4lIA0KICBkcGx5cjo6ZmlsdGVyKGlkID09IGlkX2kpDQoNCm8yY3JpdCA8LSBjYWxjTzJjcml0KERhdGEgPSBkZl9pLCBTTVIgPSBkZl9pJFNNUlsxXSwgbG93ZXN0TU8yPU5BLCBnYXBMaW1pdCA9IDQsDQogICAgICAgICAgICAgICAgICAgICBtYXgubmIuTU8yLmZvci5yZWcgPSA3KQ0KDQp2YXVsZSA8LSBvMmNyaXQkbzJjcml0DQpsb3dlc3RNTzIgPSBxdWFudGlsZShkZl9pJE1PMltkZl9pJERPID49IDgwXSwgcD0wLjA1KQ0KU01SIDwtIG8yY3JpdCRTTVINCm5iX21vMl9jb25mb3JtaW5nIDwtIG8yY3JpdCROYl9NTzJfY29uZm9ybWluZw0KcjIgPC0gbzJjcml0JHIyDQptZXRob2QgPC0gbzJjcml0JE1ldGhvZA0KcCA8LSBvMmNyaXQkUFsxXQ0KDQpwY3JpdF9tb2RlbF9kZiA8LSB0aWJibGUoDQogICAgICBpZCA9IGlkX2ksDQogICAgICBwY3JpdF92YXVsZSA9IHZhdWxlLA0KICAgICAgcGNyaXRfc21yID0gU01SLA0KICAgICAgcGNyaXRfbG93ZXN0TU8yID0gbG93ZXN0TU8yLA0KICAgICAgcGNyaXRfbmJfbW8yX2NvbmZvcm1pbmcgPSBuYl9tbzJfY29uZm9ybWluZywNCiAgICAgIHBjcml0X3IyID0gcjIsDQogICAgICBwY3JpdF9tZXRob2QgPSBtZXRob2QsDQogICAgICBwY3JpdF9wID0gcA0KICAgICkNCg0KcGNyaXRfbW9kZWxfZGZfbGlzdFtbaWRfaV1dIDwtIHBjcml0X21vZGVsX2RmDQoNCnBjcml0X21vZGVsc1tbaWRfaV1dIDwtIG8yY3JpdA0KDQp9DQoNCnBjcml0X21vZGVsX2RmIDwtIGJpbmRfcm93cyhwY3JpdF9tb2RlbF9kZl9saXN0KQ0KYGBgDQoNCiMjIyBQbG90aW5nIFBjcml0DQoNCkhlcmUncyB0aGUgcGxvdHMgZm9yIHRoZSBQY3JpdCBlc3RpbWF0ZXMgICANCg0KYGBge3IsIGVjaG89RkFMU0V9DQojIENyZWF0ZSBvdXRwdXQgZGlyZWN0b3J5IGlmIG5lZWRlZA0Kb3V0cHV0X2ZpZ19wY3JpdF9jaGFib3Rfd2QgPC0gZmlsZS5wYXRoKG91dHB1dF9maWdfd2QsICJtb2RlbF9jaGFib3QiKQ0KaWYgKCFkaXIuZXhpc3RzKG91dHB1dF9maWdfcGNyaXRfY2hhYm90X3dkKSkgew0KICBkaXIuY3JlYXRlKG91dHB1dF9maWdfcGNyaXRfY2hhYm90X3dkKQ0KfQ0KDQppZHMgPC0gY2hlY2tfcGNyaXRfZGYgJT4lIA0KICBkcGx5cjo6ZGlzdGluY3QoaWQpICU+JSANCiAgZHBseXI6OnB1bGwoKQ0KDQpwY3JpdF9jaGFib3RfbGlzdCA8LSBsaXN0KCkNCg0KIyBPcGVuIGEgc2luZ2xlIFBERiBkZXZpY2UNCnBkZihmaWxlID0gZmlsZS5wYXRoKG91dHB1dF9maWdfcGNyaXRfY2hhYm90X3dkLCAiY29tYmluZWRfY2hhYm90X3Bsb3RzLnBkZiIpLCANCiAgICB3aWR0aCA9IDgsIGhlaWdodCA9IDYpDQoNCmZvciAoaWRfaSBpbiBpZHMpIHsNCiAgDQogIHIyIDwtIHBjcml0X21vZGVsX2RmICU+JSANCiAgICBkcGx5cjo6ZmlsdGVyKGlkID09IGlkX2kpICU+JSANCiAgICBkcGx5cjo6bXV0YXRlKHBjcml0X3IyID0gcm91bmQocGNyaXRfcjIsIDMpKSAlPiUgDQogICAgZHBseXI6OnB1bGwocGNyaXRfcjIpDQogIA0KICBjb25mb3JtaW5nIDwtIHBjcml0X21vZGVsX2RmICU+JSANCiAgICBkcGx5cjo6ZmlsdGVyKGlkID09IGlkX2kpICU+JSANCiAgICBkcGx5cjo6bXV0YXRlKHBjcml0X25iX21vMl9jb25mb3JtaW5nID0gcm91bmQocGNyaXRfbmJfbW8yX2NvbmZvcm1pbmcsIDMpKSAlPiUgDQogICAgZHBseXI6OnB1bGwocGNyaXRfbmJfbW8yX2NvbmZvcm1pbmcpDQogIA0KICBQIDwtIHBjcml0X21vZGVsX2RmICU+JSANCiAgICBkcGx5cjo6ZmlsdGVyKGlkID09IGlkX2kpICU+JSANCiAgICBkcGx5cjo6bXV0YXRlKHBjcml0X3AgPSByb3VuZChwY3JpdF9wLCAzKSkgJT4lIA0KICAgIGRwbHlyOjpwdWxsKHBjcml0X3ApDQogIA0KICBTTVIgPC0gcGNyaXRfbW9kZWxfZGYgJT4lIA0KICAgIGRwbHlyOjpmaWx0ZXIoaWQgPT0gaWRfaSkgJT4lIA0KICAgIGRwbHlyOjptdXRhdGUocGNyaXRfc21yID0gcm91bmQocGNyaXRfc21yLCAzKSkgJT4lIA0KICAgIGRwbHlyOjpwdWxsKHBjcml0X3NtcikNCiAgDQogIGxvd2VzdE1PMiA8LSBwY3JpdF9tb2RlbF9kZiAlPiUgDQogICAgZHBseXI6OmZpbHRlcihpZCA9PSBpZF9pKSAlPiUgDQogICAgZHBseXI6Om11dGF0ZShwY3JpdF9sb3dlc3RNTzIgPSByb3VuZChwY3JpdF9sb3dlc3RNTzIsIDMpKSAlPiUgDQogICAgZHBseXI6OnB1bGwocGNyaXRfbG93ZXN0TU8yKQ0KICANCiAgIyBHZW5lcmF0ZSBhbmQgcmVuZGVyIHRoZSBwbG90DQogIHBsb3RPMmNyaXQobzJjcml0b2JqID0gcGNyaXRfbW9kZWxzW1tpZF9pXV0pDQogIA0KICAjIEFkZCBhIHRpdGxlDQogIG10ZXh0KA0KICAgIHRleHQgPSBwYXN0ZTAoaWRfaSksDQogICAgc2lkZSA9IDMsIGxpbmUgPSAyLCBhZGogPSAwLA0KICAgIGNvbCA9ICJibHVlIiwgZm9udCA9IDIsIGNleCA9IDEuMg0KICApDQogIA0KICBtdGV4dCgNCiAgICB0ZXh0ID0gcGFzdGUwKCJSMiA9ICIsIHIyLCAiOyBwID0gIiwgUCwgIjsgQ1AgPCBTTVIgPSAiLCBjb25mb3JtaW5nLCAiOyBTTVIgPSAiLCBTTVIsICI7IGxvd2VzdE1PMiA9ICIsbG93ZXN0TU8yKSwNCiAgICBzaWRlID0gMywgbGluZSA9IDEsIGFkaiA9IDAsDQogICAgY29sID0gImJsdWUiLCBmb250ID0gMSwgY2V4ID0gMC44DQogICkNCn0NCg0KIyBDbG9zZSB0aGUgUERGIGRldmljZSAqYWZ0ZXIqIHRoZSBsb29wDQpkZXYub2ZmKCkNCmBgYA0KICANClByaW50aW5nIGluIGh0bG0gZG9jdW1lbnQgICANCg0KYGBge3IsIGZpZy5oZWlnaHQgPSAzLCBmaWcud2lkdGg9IDR9DQppZHMgPC0gY2hlY2tfcGNyaXRfZGYgJT4lIA0KICBkcGx5cjo6ZGlzdGluY3QoaWQpICU+JSANCiAgZHBseXI6OnB1bGwoKQ0KDQpmb3IgKGlkX2kgaW4gaWRzKSB7DQogIA0KICBjb21tZW50IDwtIGNoZWNrX3Bjcml0X2RmICU+JSANCiAgICBkcGx5cjo6ZmlsdGVyKGlkID09IGlkX2kpICU+JSANCiAgICBkcGx5cjo6c2xpY2UoMSkgJT4lIA0KICAgIGRwbHlyOjptdXRhdGUoY29tbWVudCA9IGlmX2Vsc2UoaXMubmEoY29tbWVudHMpLCAiIiwgcGFzdGUwKCIjIiwgY29tbWVudHMpKSkgJT4lIA0KICAgIHB1bGwoY29tbWVudCkNCiAgDQogIHIyIDwtIHBjcml0X21vZGVsX2RmICU+JSANCiAgICBkcGx5cjo6ZmlsdGVyKGlkID09IGlkX2kpICU+JSANCiAgICBkcGx5cjo6bXV0YXRlKHBjcml0X3IyID0gcm91bmQocGNyaXRfcjIsIDMpKSAlPiUgDQogICAgZHBseXI6OnB1bGwocGNyaXRfcjIpDQogIA0KICBjb25mb3JtaW5nIDwtIHBjcml0X21vZGVsX2RmICU+JSANCiAgICBkcGx5cjo6ZmlsdGVyKGlkID09IGlkX2kpICU+JSANCiAgICBkcGx5cjo6bXV0YXRlKHBjcml0X25iX21vMl9jb25mb3JtaW5nID0gcm91bmQocGNyaXRfbmJfbW8yX2NvbmZvcm1pbmcsIDMpKSAlPiUgDQogICAgZHBseXI6OnB1bGwocGNyaXRfbmJfbW8yX2NvbmZvcm1pbmcpDQogIA0KICBQIDwtIHBjcml0X21vZGVsX2RmICU+JSANCiAgICBkcGx5cjo6ZmlsdGVyKGlkID09IGlkX2kpICU+JSANCiAgICBkcGx5cjo6bXV0YXRlKHBjcml0X3AgPSByb3VuZChwY3JpdF9wLCAzKSkgJT4lIA0KICAgIGRwbHlyOjpwdWxsKHBjcml0X3ApDQogIA0KICBTTVIgPC0gcGNyaXRfbW9kZWxfZGYgJT4lIA0KICAgIGRwbHlyOjpmaWx0ZXIoaWQgPT0gaWRfaSkgJT4lIA0KICAgIGRwbHlyOjptdXRhdGUocGNyaXRfc21yID0gcm91bmQocGNyaXRfc21yLCAzKSkgJT4lIA0KICAgIGRwbHlyOjpwdWxsKHBjcml0X3NtcikNCiAgDQogIGxvd2VzdE1PMiA8LSBwY3JpdF9tb2RlbF9kZiAlPiUgDQogICAgZHBseXI6OmZpbHRlcihpZCA9PSBpZF9pKSAlPiUgDQogICAgZHBseXI6Om11dGF0ZShwY3JpdF9sb3dlc3RNTzIgPSByb3VuZChwY3JpdF9sb3dlc3RNTzIsIDMpKSAlPiUgDQogICAgZHBseXI6OnB1bGwocGNyaXRfbG93ZXN0TU8yKQ0KICANCiAgIyBHZW5lcmF0ZSBhbmQgcmVuZGVyIHRoZSBwbG90DQogIHBsb3RPMmNyaXQobzJjcml0b2JqID0gcGNyaXRfbW9kZWxzW1tpZF9pXV0pDQogIA0KICAjIEFkZCBhIHRpdGxlDQogIG10ZXh0KA0KICAgIHRleHQgPSBwYXN0ZTAoaWRfaSwgIiAiLCBjb21tZW50KSwNCiAgICBzaWRlID0gMywgbGluZSA9IDIsIGFkaiA9IDAsDQogICAgY29sID0gImJsdWUiLCBmb250ID0gMiwgY2V4ID0gMS4yDQogICkNCiAgDQogIG10ZXh0KA0KICAgIHRleHQgPSBwYXN0ZTAoIlIyID0gIiwgcjIsICI7IHAgPSAiLCBQLCAiOyBDUCA8IFNNUiA9ICIsIGNvbmZvcm1pbmcsICI7IFNNUiA9ICIsIFNNUiwgIjsgbG93ZXN0TU8yID0gIixsb3dlc3RNTzIpLA0KICAgIHNpZGUgPSAzLCBsaW5lID0gMSwgYWRqID0gMCwNCiAgICBjb2wgPSAiYmx1ZSIsIGZvbnQgPSAwLjUsIGNleCA9IDAuOA0KICApDQp9DQpgYGANCg0KIyMjIFBjcml0IHJ1bGVzIA0KDQpXZSBuZWVkIHRvIHNldCBzb21lIHJ1bGVzIGFzIHRvIHdoZW4gdGhlIFBjcml0IGVzdGltYXRlcyBhcmUgcmVsaWFibGUsIGFzIGl0IHNlZW1zIG1hbnkgb2Ygb3VyIGZpc2ggZG8gbm90IHJlYWNoIGEgUGNyaXQuICAgDQoNCldlIGNhbiBmaWx0ZXIgZm9yIG9ubHkgY2FzZXMgd2VyZSBhdCB0aGUgbG93ZXN0IE8yIHZhbHVlIHRocmVlIGNvbnNlY3V0aXZlIE1PMiBtZWFzdXJlcyBmdWxsIGJlbG93IG91ciBTTVIgYW5kIGZpZnRoIHBlcmNlbnRpbGUgb2YgdGhlIE1PMiB2YWx1ZXMgb2JzZXJ2ZWQgYXQgZGlzc29sdmVkIE8yIGxldmVscyA+IDgwJS4gSW4gdGhlIG1vZGVsIG91dHB1dCB0aGVzZSBhcmUgY2FsbGVkIG5iX21vMl9jb25mb3JtaW5nIHBvaW50cy4gV2UgY2FuIHRoZSB2aXN1YWxseSBpbnNwZWN0IHRoZXNlIHRvIHNlZSBpZiBhIFBjcml0IGlzIHByZXNlbnQuIA0KDQpgYGB7cn0NCnBjcml0X2xpc3QgPC0gcGNyaXRfbW9kZWxfZGYgJT4lIA0KICBkcGx5cjo6ZmlsdGVyKHBjcml0X25iX21vMl9jb25mb3JtaW5nID4gMikgJT4lIA0KICBwdWxsKGlkKQ0KDQpwYXN0ZTAoIkJhc2VkIG9uIHRoaXMgcnVsZSB0aGVyZSBhcmUgIiwgbGVuZ3RoKHBjcml0X2xpc3QpLCAiIGZpc2ggd2l0aCBwb3NzaWJsZSBQY3JpdHMuIikNCmBgYA0KICANCkhlcmUgYXJlIHRoZSBwbG90cyBvZiB0aGVzZSAxMyBmaXNoIGZvciB2aXN1YWwgY29uZmlybWF0aW9uICAgDQoNCmBgYHtyLCBmaWcuaGVpZ2h0ID0gMywgZmlnLndpZHRoPSA0fQ0KZm9yIChpZF9pIGluIHBjcml0X2xpc3QpIHsNCiAgDQogIGNvbW1lbnQgPC0gY2hlY2tfcGNyaXRfZGYgJT4lIA0KICAgIGRwbHlyOjpmaWx0ZXIoaWQgPT0gaWRfaSkgJT4lIA0KICAgIGRwbHlyOjpzbGljZSgxKSAlPiUgDQogICAgZHBseXI6Om11dGF0ZShjb21tZW50ID0gaWZfZWxzZShpcy5uYShjb21tZW50cyksICIiLCBwYXN0ZTAoIiMiLCBjb21tZW50cykpKSAlPiUgDQogICAgcHVsbChjb21tZW50KQ0KICANCiAgcjIgPC0gcGNyaXRfbW9kZWxfZGYgJT4lIA0KICAgIGRwbHlyOjpmaWx0ZXIoaWQgPT0gaWRfaSkgJT4lIA0KICAgIGRwbHlyOjptdXRhdGUocGNyaXRfcjIgPSByb3VuZChwY3JpdF9yMiwgMykpICU+JSANCiAgICBkcGx5cjo6cHVsbChwY3JpdF9yMikNCiAgDQogIGNvbmZvcm1pbmcgPC0gcGNyaXRfbW9kZWxfZGYgJT4lIA0KICAgIGRwbHlyOjpmaWx0ZXIoaWQgPT0gaWRfaSkgJT4lIA0KICAgIGRwbHlyOjptdXRhdGUocGNyaXRfbmJfbW8yX2NvbmZvcm1pbmcgPSByb3VuZChwY3JpdF9uYl9tbzJfY29uZm9ybWluZywgMykpICU+JSANCiAgICBkcGx5cjo6cHVsbChwY3JpdF9uYl9tbzJfY29uZm9ybWluZykNCiAgDQogIFAgPC0gcGNyaXRfbW9kZWxfZGYgJT4lIA0KICAgIGRwbHlyOjpmaWx0ZXIoaWQgPT0gaWRfaSkgJT4lIA0KICAgIGRwbHlyOjptdXRhdGUocGNyaXRfcCA9IHJvdW5kKHBjcml0X3AsIDMpKSAlPiUgDQogICAgZHBseXI6OnB1bGwocGNyaXRfcCkNCiAgDQogIFNNUiA8LSBwY3JpdF9tb2RlbF9kZiAlPiUgDQogICAgZHBseXI6OmZpbHRlcihpZCA9PSBpZF9pKSAlPiUgDQogICAgZHBseXI6Om11dGF0ZShwY3JpdF9zbXIgPSByb3VuZChwY3JpdF9zbXIsIDMpKSAlPiUgDQogICAgZHBseXI6OnB1bGwocGNyaXRfc21yKQ0KICANCiAgbG93ZXN0TU8yIDwtIHBjcml0X21vZGVsX2RmICU+JSANCiAgICBkcGx5cjo6ZmlsdGVyKGlkID09IGlkX2kpICU+JSANCiAgICBkcGx5cjo6bXV0YXRlKHBjcml0X2xvd2VzdE1PMiA9IHJvdW5kKHBjcml0X2xvd2VzdE1PMiwgMykpICU+JSANCiAgICBkcGx5cjo6cHVsbChwY3JpdF9sb3dlc3RNTzIpDQogIA0KICAjIEdlbmVyYXRlIGFuZCByZW5kZXIgdGhlIHBsb3QNCiAgcGxvdE8yY3JpdChvMmNyaXRvYmogPSBwY3JpdF9tb2RlbHNbW2lkX2ldXSkNCiAgDQogICMgQWRkIGEgdGl0bGUNCiAgbXRleHQoDQogICAgdGV4dCA9IHBhc3RlMChpZF9pLCAiICIsIGNvbW1lbnQpLA0KICAgIHNpZGUgPSAzLCBsaW5lID0gMiwgYWRqID0gMCwNCiAgICBjb2wgPSAiYmx1ZSIsIGZvbnQgPSAyLCBjZXggPSAxLjINCiAgKQ0KICANCiAgbXRleHQoDQogICAgdGV4dCA9IHBhc3RlMCgiUjIgPSAiLCByMiwgIjsgcCA9ICIsIFAsICI7IENQIDwgU01SID0gIiwgY29uZm9ybWluZywgIjsgU01SID0gIiwgU01SLCAiOyBsb3dlc3RNTzIgPSAiLGxvd2VzdE1PMiksDQogICAgc2lkZSA9IDMsIGxpbmUgPSAxLCBhZGogPSAwLA0KICAgIGNvbCA9ICJibHVlIiwgZm9udCA9IDEsIGNleCA9IDAuOA0KICApDQp9DQpgYGANCg0KIyMjIFBjcml0cyBudW1iZXJzDQoNCkJhc2VkIG9uIHZpc3VhbCBjaGVja3MgdGhlIGZvbGxvd2luZyBmaXNoIGRvIGhhdmUgY2xlYXIgUGNyaXQgdmFsdWVzLg0KDQpgYGB7cn0NCmRvX2hhdmVfcGNyaXQgPC0gYygiYV85XzIxbm92XzMiLCAiYl8wXzI0bm92XzEiLCAiYl8wXzI0bm92XzIiLCAiYl8wXzI1bm92XzEiLCAiYl8wXzI1bm92XzMiLCAiYl8wXzI2X25vdl8xIiwgImJfOV8yMV9ub3ZfMSIsICJiXzlfMjFub3ZfMiIsICJiXzlfMjFub3ZfMyIsICJkXzBfMjFub3ZfMyIpDQoNCm5fcGNyaXQgPC0gbGVuZ3RoKGRvX2hhdmVfcGNyaXQpDQoNCmhhdmVfcGNpcnQgPC0gcGNyaXRfbW9kZWxfZGYgJT4lIA0KICBkcGx5cjo6ZmlsdGVyKGlkICVpbiUgZG9faGF2ZV9wY3JpdCkNCg0KbWVhbl9wY3JpdCA8LSBoYXZlX3BjaXJ0ICU+JSANCiAgZHBseXI6OnJlZnJhbWUobWVhbiA9IG1lYW4ocGNyaXRfdmF1bGUpKSAlPiUgDQogIHB1bGwobWVhbikgJT4lIA0KICByb3VuZCguLCAyKQ0KDQptaW5fcGNyaXQgPC0gaGF2ZV9wY2lydCAlPiUgDQogIGRwbHlyOjpyZWZyYW1lKG1pbiA9IG1pbihwY3JpdF92YXVsZSkpICU+JSANCiAgcHVsbChtaW4pICU+JSANCiAgcm91bmQoLiwgMikNCg0KbWF4X3Bjcml0IDwtIGhhdmVfcGNpcnQgJT4lIA0KICBkcGx5cjo6cmVmcmFtZShtYXggPSBtYXgocGNyaXRfdmF1bGUpKSAlPiUgDQogIHB1bGwobWF4KSAlPiUgDQogIHJvdW5kKC4sIDIpDQoNCnByaW50KHBhc3RlMCgiVGhlcmUgYXJlICIsIG5fcGNyaXQsICIgZmlzaCB3aXRoIGlkZW50aWZpZWQgUGNyaXRzIGFuZCB0aGUgbWVhbiBQY3JpdCBpcyAiLCBtZWFuX3Bjcml0LCAiIChyYW5nZTogIiwgDQogICAgICAgICAgICAgbWluX3Bjcml0LCAi4oCTIiwgbWF4X3Bjcml0LCAiKSIpKQ0KYGBgDQogIA0KIyMhTk9URTogQ2hlY2sgbWFzcyBhbmQgc2FpbGl0eSwgYWRkIHRoZSB2aXN1YWwgYXNzZXNzbWVudCByZXN1bHRzIA0KDQpgYGB7cn0NCmRvX2hhdmVfcGNyaXQgPC0gYygiYV85XzIxbm92XzMiLCAiYl8wXzI0bm92XzEiLCAiYl8wXzI0bm92XzIiLCAiYl8wXzI1bm92XzEiLCAiYl8wXzI1bm92XzMiLCAiYl8wXzI2X25vdl8xIiwgImJfOV8yMV9ub3ZfMSIsICJiXzlfMjFub3ZfMiIsICJiXzlfMjFub3ZfMyIsICJkXzBfMjFub3ZfMyIpDQoNCnNsb3BlX3RpZHkgJT4lIA0KICBkcGx5cjo6ZmlsdGVyKGlkICVpbiUgZG9faGF2ZV9wY3JpdCkgJT4lIA0KICBkcGx5cjo6Z3JvdXBfYnkoaWQpICU+JSANCiAgZHBseXI6OnNsaWNlKDEpDQpgYGANCg0KDQo8IS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0+DQojIFJlZmVyZW5jZXMgDQo8IS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0+DQogICANCl5bMV1eIENsYWlyZWF1eCwgRy4gYW5kIENoYWJvdCwgRC4gKDIwMTYpIFJlc3BvbnNlcyBieSBmaXNoZXMgdG8gZW52aXJvbm1lbnRhbCBoeXBveGlhOiBpbnRlZ3JhdGlvbiB0aHJvdWdoIEZyeSdzIGNvbmNlcHQgb2YgYWVyb2JpYyBtZXRhYm9saWMgc2NvcGUuIEpvdXJuYWwgb2YgRmlzaCBCaW9sb2d5IGh0dHBzOi8vZG9pLm9yZy8xMC4xMTExL2pmYi4xMjgzMyAgDQoNCl5bMl1eIFVyYmluYSBNQSwgR2xvdmVyIENOLCBhbmQgRm9yc3RlciBNRSwgKDIwMTIpIEEgbm92ZWwgb3h5Y29uZm9ybWluZyByZXNwb25zZSBpbiB0aGUgZnJlc2h3YXRlciBmaXNoICpHYWxheGlhcyBtYWN1bGF0dXMqLiBDb21wYXJhdGl2ZSBCaW9jaGVtaXN0cnkgYW5kIFBoeXNpb2xvZ3kgUGFydCBBOiBNb2xlY3VsYXIgJiBJbnRlZ3JhdGl2ZSBQaHlzaW9sb2d5LiBodHRwczovL2RvaS5vcmcvMTAuMTAxNi9qLmNicGEuMjAxMS4xMS4wMTEgIA0KDQpeWzNdXiBQaWNrIEpMLCBOYWthZ2F3YSBTLCBhbmQgTm9ibGUgRFdBICgyMDE4KSBSZXByb2R1Y2libGUsIGZsZXhpYmxlIGFuZCBoaWdoLXRocm91Z2hwdXQgZGF0YSBleHRyYWN0aW9uIGZyb20gcHJpbWFyeSBsaXRlcmF0dXJlOiBUaGUgbWV0YURpZ2l0aXNlIHIgcGFja2FnZS4gaHR0cHM6Ly9kb2kub3JnLzEwLjExMTEvMjA0MS0yMTBYLjEzMTE4ICANCg0KDQo8IS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0+DQojIFNlc3Npb24gaW5mb3JtYXRpb24NCjwhLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLT4NCg0KSGVyZSBpcyBhIGRldGFpbGVkIGxpc3Qgb2YgdGhlIHNlc3Npb24gaW5mb3JtYXRpb24gIA0KDQpgYGB7cn0NCnNlc3Npb25pbmZvOjpzZXNzaW9uX2luZm8oKQ0KYGBgDQo=